diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d142185008ff..ef90d000d7f4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1 @@ -* @aeneasr @zepatrik @hperl - -/docs/ @ory/documenters +* @aeneasr @ory/product-development diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml index b3db2a2d3de2..d0b98a4c15ef 100644 --- a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -1,20 +1,20 @@ # AUTO-GENERATED, DO NOT EDIT! # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/BUG-REPORT.yml -description: 'Create a bug report' +description: "Create a bug report" labels: - bug -name: 'Bug Report' +name: "Bug Report" body: - attributes: value: "Thank you for taking the time to fill out this bug report!\n" type: markdown - attributes: - label: 'Preflight checklist' + label: "Preflight checklist" options: - label: - 'I could not find a solution in the existing issues, docs, nor - discussions.' + "I could not find a solution in the existing issues, docs, nor + discussions." required: true - label: "I agree to follow this project's [Code of @@ -25,24 +25,24 @@ body: Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - label: - 'I have joined the [Ory Community Slack](https://slack.ory.sh).' + "I have joined the [Ory Community Slack](https://slack.ory.sh)." - label: - 'I am signed up to the [Ory Security Patch - Newsletter](https://www.ory.sh/l/sign-up-newsletter).' + "I am signed up to the [Ory Security Patch + Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes - attributes: description: - 'Enter the slug or API URL of the affected Ory Network project. Leave - empty when you are self-hosting.' - label: 'Ory Network Project' - placeholder: 'https://.projects.oryapis.com' + "Enter the slug or API URL of the affected Ory Network project. Leave + empty when you are self-hosting." + label: "Ory Network Project" + placeholder: "https://.projects.oryapis.com" id: ory-network-project type: input - attributes: - description: 'A clear and concise description of what the bug is.' - label: 'Describe the bug' - placeholder: 'Tell us what you see!' + description: "A clear and concise description of what the bug is." + label: "Describe the bug" + placeholder: "Tell us what you see!" id: describe-bug type: textarea validations: @@ -56,17 +56,17 @@ body: 1. Run `docker run ....` 2. Make API Request to with `curl ...` 3. Request fails with response: `{"some": "error"}` - label: 'Reproducing the bug' + label: "Reproducing the bug" id: reproduce-bug type: textarea validations: required: true - attributes: description: - 'Please copy and paste any relevant log output. This will be + "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. Please - redact any sensitive information' - label: 'Relevant log output' + redact any sensitive information" + label: "Relevant log output" render: shell placeholder: | log=error .... @@ -74,10 +74,10 @@ body: type: textarea - attributes: description: - 'Please copy and paste any relevant configuration. This will be + "Please copy and paste any relevant configuration. This will be automatically formatted into code, so no need for backticks. Please - redact any sensitive information!' - label: 'Relevant configuration' + redact any sensitive information!" + label: "Relevant configuration" render: yml placeholder: | server: @@ -86,14 +86,14 @@ body: id: config type: textarea - attributes: - description: 'What version of our software are you running?' + description: "What version of our software are you running?" label: Version id: version type: input validations: required: true - attributes: - label: 'On which operating system are you observing this issue?' + label: "On which operating system are you observing this issue?" options: - Ory Network - macOS @@ -104,19 +104,19 @@ body: id: operating-system type: dropdown - attributes: - label: 'In which environment are you deploying?' + label: "In which environment are you deploying?" options: - Ory Network - Docker - - 'Docker Compose' - - 'Kubernetes with Helm' + - "Docker Compose" + - "Kubernetes with Helm" - Kubernetes - Binary - Other id: deployment type: dropdown - attributes: - description: 'Add any other context about the problem here.' + description: "Add any other context about the problem here." label: Additional Context id: additional type: textarea diff --git a/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml b/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml index a6a86f36dcc7..0fb22fad2b48 100644 --- a/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml +++ b/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml @@ -2,10 +2,10 @@ # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml description: - 'A design document is needed for non-trivial changes to the code base.' + "A design document is needed for non-trivial changes to the code base." labels: - rfc -name: 'Design Document' +name: "Design Document" body: - attributes: value: | @@ -21,11 +21,11 @@ body: after code reviews, and your pull requests will be merged faster. type: markdown - attributes: - label: 'Preflight checklist' + label: "Preflight checklist" options: - label: - 'I could not find a solution in the existing issues, docs, nor - discussions.' + "I could not find a solution in the existing issues, docs, nor + discussions." required: true - label: "I agree to follow this project's [Code of @@ -36,24 +36,24 @@ body: Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - label: - 'I have joined the [Ory Community Slack](https://slack.ory.sh).' + "I have joined the [Ory Community Slack](https://slack.ory.sh)." - label: - 'I am signed up to the [Ory Security Patch - Newsletter](https://www.ory.sh/l/sign-up-newsletter).' + "I am signed up to the [Ory Security Patch + Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes - attributes: description: - 'Enter the slug or API URL of the affected Ory Network project. Leave - empty when you are self-hosting.' - label: 'Ory Network Project' - placeholder: 'https://.projects.oryapis.com' + "Enter the slug or API URL of the affected Ory Network project. Leave + empty when you are self-hosting." + label: "Ory Network Project" + placeholder: "https://.projects.oryapis.com" id: ory-network-project type: input - attributes: description: | 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. - label: 'Context and scope' + label: "Context and scope" id: scope type: textarea validations: @@ -62,7 +62,7 @@ body: - attributes: description: | 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. - label: 'Goals and non-goals' + label: "Goals and non-goals" id: goals type: textarea validations: @@ -74,7 +74,7 @@ body: 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. 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. - label: 'The design' + label: "The design" id: design type: textarea validations: @@ -83,21 +83,21 @@ body: - attributes: description: | 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. - label: 'APIs' + label: "APIs" id: apis type: textarea - attributes: description: | 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. - label: 'Data storage' + label: "Data storage" id: persistence type: textarea - attributes: description: | 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. - label: 'Code and pseudo-code' + label: "Code and pseudo-code" id: pseudocode type: textarea @@ -110,7 +110,7 @@ body: 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. 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. - label: 'Degree of constraint' + label: "Degree of constraint" id: constrait type: textarea diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml index 7c023c2f48b3..e0e42201886e 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml @@ -2,10 +2,10 @@ # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml description: - 'Suggest an idea for this project without a plan for implementation' + "Suggest an idea for this project without a plan for implementation" labels: - feat -name: 'Feature Request' +name: "Feature Request" body: - attributes: value: | @@ -14,11 +14,11 @@ body: 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! type: markdown - attributes: - label: 'Preflight checklist' + label: "Preflight checklist" options: - label: - 'I could not find a solution in the existing issues, docs, nor - discussions.' + "I could not find a solution in the existing issues, docs, nor + discussions." required: true - label: "I agree to follow this project's [Code of @@ -29,24 +29,24 @@ body: Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - label: - 'I have joined the [Ory Community Slack](https://slack.ory.sh).' + "I have joined the [Ory Community Slack](https://slack.ory.sh)." - label: - 'I am signed up to the [Ory Security Patch - Newsletter](https://www.ory.sh/l/sign-up-newsletter).' + "I am signed up to the [Ory Security Patch + Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes - attributes: description: - 'Enter the slug or API URL of the affected Ory Network project. Leave - empty when you are self-hosting.' - label: 'Ory Network Project' - placeholder: 'https://.projects.oryapis.com' + "Enter the slug or API URL of the affected Ory Network project. Leave + empty when you are self-hosting." + label: "Ory Network Project" + placeholder: "https://.projects.oryapis.com" id: ory-network-project type: input - attributes: description: - 'Is your feature request related to a problem? Please describe.' - label: 'Describe your problem' + "Is your feature request related to a problem? Please describe." + label: "Describe your problem" placeholder: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]" @@ -59,20 +59,20 @@ body: Describe the solution you'd like placeholder: | A clear and concise description of what you want to happen. - label: 'Describe your ideal solution' + label: "Describe your ideal solution" id: solution type: textarea validations: required: true - attributes: description: "Describe alternatives you've considered" - label: 'Workarounds or alternatives' + label: "Workarounds or alternatives" id: alternatives type: textarea validations: required: true - attributes: - description: 'What version of our software are you running?' + description: "What version of our software are you running?" label: Version id: version type: input @@ -80,7 +80,7 @@ body: required: true - attributes: description: - 'Add any other context or screenshots about the feature request here.' + "Add any other context or screenshots about the feature request here." label: Additional Context id: additional type: textarea diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9fc74bd6397f..fd01491e2a9a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -85,6 +85,9 @@ jobs: uses: sonatype-nexus-community/nancy-github-action@v1.0.2 with: nancyVersion: v1.0.42 + - run: | + sudo apt-get update + name: apt-get update - run: npm install name: Install node deps - name: Run golangci-lint @@ -158,6 +161,9 @@ jobs: - uses: ory/ci/checkout@master with: fetch-depth: 2 + - run: | + sudo apt-get update + name: apt-get update - run: | npm ci cd test/e2e; npm ci @@ -261,6 +267,9 @@ jobs: - uses: ory/ci/checkout@master with: fetch-depth: 2 + - run: | + sudo apt-get update + name: apt-get update - run: | npm ci cd test/e2e; npm ci diff --git a/.github/workflows/cve-scan.yaml b/.github/workflows/cve-scan.yaml index 28e88e24fd28..b8aa0197182a 100644 --- a/.github/workflows/cve-scan.yaml +++ b/.github/workflows/cve-scan.yaml @@ -1,3 +1,6 @@ +# AUTO-GENERATED, DO NOT EDIT! +# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/server/.github/workflows/cve-scan.yaml + name: Docker Image Scanners on: workflow_dispatch: @@ -24,7 +27,28 @@ jobs: id: vars shell: bash run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> "${GITHUB_ENV}" + # Store values in local variables + SHA_SHORT=$(git rev-parse --short HEAD) + REPO_NAME=${{ github.event.repository.name }} + + # Append -sqlite to SHA_SHORT if repo is hydra + if [ "${REPO_NAME}" = "hydra" ]; then + echo "Repo is hydra, appending -sqlite to SHA_SHORT" + IMAGE_NAME="oryd/${REPO_NAME}:${SHA_SHORT}-sqlite" + else + echo "Repo is not hydra, using default IMAGE_NAME" + IMAGE_NAME="oryd/${REPO_NAME}:${SHA_SHORT}" + fi + + # Output values for debugging + echo "Values to be set:" + echo "SHA_SHORT: ${SHA_SHORT}" + echo "REPO_NAME: ${REPO_NAME}" + echo "IMAGE_NAME: ${IMAGE_NAME}" + + # Set GitHub Environment variables + echo "SHA_SHORT=${SHA_SHORT}" >> "${GITHUB_ENV}" + echo "IMAGE_NAME=${IMAGE_NAME}" >> "${GITHUB_ENV}" - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx @@ -34,7 +58,6 @@ jobs: run: | IMAGE_TAG="${{ env.SHA_SHORT }}" make docker - # Add GitHub authentication for Trivy - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: @@ -42,7 +65,6 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # Configure Trivy - name: Configure Trivy run: | mkdir -p $HOME/.cache/trivy @@ -53,7 +75,7 @@ jobs: uses: anchore/scan-action@v5 id: grype-scan with: - image: oryd/kratos:${{ env.SHA_SHORT }} + image: ${{ env.IMAGE_NAME }} fail-build: true severity-cutoff: high add-cpes-if-none: true @@ -69,12 +91,20 @@ jobs: uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.grype-scan.outputs.sarif }} - + - name: Kubescape scanner + uses: kubescape/github-action@main + id: kubescape + with: + image: ${{ env.IMAGE_NAME }} + verbose: true + format: pretty-printer + # can't whitelist CVE yet: https://github.com/kubescape/kubescape/pull/1568 + severityThreshold: critical - name: Trivy Scanner uses: aquasecurity/trivy-action@master if: ${{ always() }} with: - image-ref: oryd/kratos:${{ env.SHA_SHORT }} + image-ref: ${{ env.IMAGE_NAME }} format: "table" exit-code: "42" ignore-unfixed: true @@ -84,12 +114,13 @@ jobs: env: TRIVY_SKIP_JAVA_DB_UPDATE: "true" TRIVY_DISABLE_VEX_NOTICE: "true" + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db - name: Dockle Linter uses: erzz/dockle-action@v1 if: ${{ always() }} with: - image: oryd/kratos:${{ env.SHA_SHORT }} + image: ${{ env.IMAGE_NAME }} exit-code: 42 failure-threshold: high - name: Hadolint diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 9d1589506da2..171e019634ba 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -1,3 +1,6 @@ +# AUTO-GENERATED, DO NOT EDIT! +# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/licenses.yml + name: Licenses on: @@ -8,14 +11,24 @@ on: - master jobs: - check: + licenses: + name: License compliance runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v2 + - name: Install script + uses: ory/ci/licenses/setup@master with: - go-version: "1.23" - - uses: actions/setup-node@v2 + token: ${{ secrets.ORY_BOT_PAT || secrets.GITHUB_TOKEN }} + - name: Check licenses + uses: ory/ci/licenses/check@master + - name: Write, commit, push licenses + uses: ory/ci/licenses/write@master + if: + ${{ github.ref == 'refs/heads/main' || github.ref == + 'refs/heads/master' }} with: - node-version: "18" - - run: make licenses + author-email: + ${{ secrets.ORY_BOT_PAT && + '60093411+ory-bot@users.noreply.github.com' || + format('{0}@users.noreply.github.com', github.actor) }} + author-name: ${{ secrets.ORY_BOT_PAT && 'ory-bot' || github.actor }} diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml index 5d25a715ddd8..218b9c6e62a1 100644 --- a/.github/workflows/milestone.yml +++ b/.github/workflows/milestone.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: token: ${{ secrets.TOKEN_PRIVILEGED }} - name: Milestone Documentation Generator diff --git a/.reports/dep-licenses.csv b/.reports/dep-licenses.csv new file mode 100644 index 000000000000..efcfe9f0d706 --- /dev/null +++ b/.reports/dep-licenses.csv @@ -0,0 +1,532 @@ +"code.dny.dev/ssrf","MIT" +"dario.cat/mergo","BSD-3-Clause" +"filippo.io/edwards25519","BSD-3-Clause" +"github.com/Masterminds/goutils","Apache-2.0" +"github.com/Masterminds/semver/v3","MIT" +"github.com/Masterminds/sprig/v3","MIT" +"github.com/Nvveen/Gotty","BSD-2-Clause" +"github.com/arbovm/levenshtein","BSD-3-Clause" +"github.com/asaskevich/govalidator","MIT" +"github.com/avast/retry-go/v4","MIT" +"github.com/aymerick/douceur","MIT" +"github.com/beorn7/perks/quantile","MIT" +"github.com/boombuler/barcode","MIT" +"github.com/bwmarrin/discordgo","BSD-3-Clause" +"github.com/cenkalti/backoff","MIT" +"github.com/cenkalti/backoff/v4","MIT" +"github.com/cespare/xxhash/v2","MIT" +"github.com/cockroachdb/cockroach-go/v2/crdb","Apache-2.0" +"github.com/containerd/continuity/pathdriver","Apache-2.0" +"github.com/coreos/go-oidc/v3/oidc","Apache-2.0" +"github.com/davecgh/go-spew/spew","ISC" +"github.com/dghubble/oauth1","MIT" +"github.com/dgraph-io/ristretto","Apache-2.0" +"github.com/dgraph-io/ristretto/v2","Apache-2.0" +"github.com/dgraph-io/ristretto/v2/z","MIT" +"github.com/dgraph-io/ristretto/z","MIT" +"github.com/docker/cli","Apache-2.0" +"github.com/docker/docker","Apache-2.0" +"github.com/docker/go-connections/nat","Apache-2.0" +"github.com/docker/go-units","Apache-2.0" +"github.com/dustin/go-humanize","MIT" +"github.com/evanphx/json-patch/v5","BSD-3-Clause" +"github.com/fatih/color","MIT" +"github.com/fatih/structs","MIT" +"github.com/felixge/fgprof","MIT" +"github.com/felixge/httpsnoop","MIT" +"github.com/fsnotify/fsnotify","BSD-3-Clause" +"github.com/fxamacker/cbor/v2","MIT" +"github.com/gabriel-vasile/mimetype","MIT" +"github.com/go-crypt/crypt","MIT" +"github.com/go-crypt/x","BSD-3-Clause" +"github.com/go-faker/faker/v4/pkg/slice","MIT" +"github.com/go-jose/go-jose/v3","Apache-2.0" +"github.com/go-jose/go-jose/v3/json","BSD-3-Clause" +"github.com/go-jose/go-jose/v4","Apache-2.0" +"github.com/go-jose/go-jose/v4/json","BSD-3-Clause" +"github.com/go-logr/logr","Apache-2.0" +"github.com/go-logr/stdr","Apache-2.0" +"github.com/go-openapi/errors","Apache-2.0" +"github.com/go-openapi/jsonpointer","Apache-2.0" +"github.com/go-openapi/strfmt","Apache-2.0" +"github.com/go-openapi/swag","Apache-2.0" +"github.com/go-playground/locales","MIT" +"github.com/go-playground/universal-translator","MIT" +"github.com/go-playground/validator/v10","MIT" +"github.com/go-sql-driver/mysql","MPL-2.0" +"github.com/go-webauthn/webauthn","BSD-3-Clause" +"github.com/go-webauthn/x/revoke","BSD-2-Clause" +"github.com/gobuffalo/envy","MIT" +"github.com/gobuffalo/fizz","MIT" +"github.com/gobuffalo/flect","MIT" +"github.com/gobuffalo/github_flavored_markdown","MIT" +"github.com/gobuffalo/github_flavored_markdown/internal/russross/blackfriday","BSD-2-Clause" +"github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/sanitized_anchor_name","MIT" +"github.com/gobuffalo/helpers","MIT" +"github.com/gobuffalo/nulls","MIT" +"github.com/gobuffalo/plush/v4","MIT" +"github.com/gobuffalo/pop/v6","MIT" +"github.com/gobuffalo/tags/v3","MIT" +"github.com/gobuffalo/validate/v3","MIT" +"github.com/gobwas/glob","MIT" +"github.com/goccy/go-yaml","MIT" +"github.com/gofrs/uuid","MIT" +"github.com/gogo/protobuf","BSD-3-Clause" +"github.com/golang-jwt/jwt/v4","MIT" +"github.com/golang-jwt/jwt/v5","MIT" +"github.com/golang/gddo/httputil","BSD-3-Clause" +"github.com/golang/protobuf","BSD-3-Clause" +"github.com/google/go-github/v38/github","BSD-3-Clause" +"github.com/google/go-jsonnet","Apache-2.0" +"github.com/google/go-querystring/query","BSD-3-Clause" +"github.com/google/go-tpm","Apache-2.0" +"github.com/google/pprof/profile","Apache-2.0" +"github.com/google/shlex","Apache-2.0" +"github.com/google/uuid","BSD-3-Clause" +"github.com/gorilla/css/scanner","BSD-3-Clause" +"github.com/gorilla/securecookie","BSD-3-Clause" +"github.com/gorilla/sessions","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"github.com/grpc-ecosystem/go-grpc-prometheus","Apache-2.0" +"github.com/grpc-ecosystem/grpc-gateway/v2","BSD-3-Clause" +"github.com/gtank/cryptopasta","CC0-1.0" +"github.com/hashicorp/go-cleanhttp","MPL-2.0" +"github.com/hashicorp/go-retryablehttp","MPL-2.0" +"github.com/hashicorp/golang-lru/v2","MPL-2.0" +"github.com/hashicorp/golang-lru/v2/simplelru","BSD-3-Clause" +"github.com/huandu/xstrings","MIT" +"github.com/imdario/mergo","BSD-3-Clause" +"github.com/inhies/go-bytesize","BSD-3-Clause" +"github.com/jackc/chunkreader/v2","MIT" +"github.com/jackc/pgconn","MIT" +"github.com/jackc/pgio","MIT" +"github.com/jackc/pgpassfile","MIT" +"github.com/jackc/pgproto3/v2","MIT" +"github.com/jackc/pgservicefile","MIT" +"github.com/jackc/pgx/v5","MIT" +"github.com/jackc/puddle/v2","MIT" +"github.com/jmoiron/sqlx","MIT" +"github.com/joho/godotenv","MIT" +"github.com/josharian/intern","MIT" +"github.com/julienschmidt/httprouter","BSD-3-Clause" +"github.com/kballard/go-shellquote","MIT" +"github.com/knadh/koanf/maps","MIT" +"github.com/knadh/koanf/parsers/json","MIT" +"github.com/knadh/koanf/parsers/toml","MIT" +"github.com/knadh/koanf/parsers/yaml","MIT" +"github.com/knadh/koanf/providers/posflag","MIT" +"github.com/knadh/koanf/v2","MIT" +"github.com/leodido/go-urn","MIT" +"github.com/lestrrat-go/backoff/v2","MIT" +"github.com/lestrrat-go/blackmagic","MIT" +"github.com/lestrrat-go/httpcc","MIT" +"github.com/lestrrat-go/iter","MIT" +"github.com/lestrrat-go/jwx","MIT" +"github.com/lestrrat-go/option","MIT" +"github.com/lib/pq","MIT" +"github.com/luna-duclos/instrumentedsql","MIT" +"github.com/mailru/easyjson","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"github.com/matttproud/golang_protobuf_extensions/pbutil","Apache-2.0" +"github.com/microcosm-cc/bluemonday","BSD-3-Clause" +"github.com/mitchellh/copystructure","MIT" +"github.com/mitchellh/mapstructure","MIT" +"github.com/mitchellh/reflectwalk","MIT" +"github.com/moby/docker-image-spec/specs-go/v1","Apache-2.0" +"github.com/moby/term","Apache-2.0" +"github.com/mohae/deepcopy","MIT" +"github.com/montanaflynn/stats","MIT" +"github.com/nyaruka/phonenumbers","MIT" +"github.com/oklog/ulid","Apache-2.0" +"github.com/opencontainers/go-digest","Apache-2.0" +"github.com/opencontainers/image-spec/specs-go","Apache-2.0" +"github.com/opencontainers/runc/libcontainer/user","Apache-2.0" +"github.com/openzipkin/zipkin-go/model","Apache-2.0" +"github.com/ory/analytics-go/v5","MIT" +"github.com/ory/dockertest/v3","Apache-2.0" +"github.com/ory/dockertest/v3/docker","BSD-2-Clause" +"github.com/ory/graceful","Apache-2.0" +"github.com/ory/herodot","Apache-2.0" +"github.com/ory/hydra-client-go/v2","Apache-2.0" +"github.com/ory/jsonschema/v3","BSD-3-Clause" +"github.com/ory/kratos","Apache-2.0" +"github.com/ory/mail/v3","MIT" +"github.com/ory/nosurf","MIT" +"github.com/ory/x","Apache-2.0" +"github.com/ory/x/reqlog","MIT" +"github.com/pelletier/go-toml","MIT" +"github.com/pelletier/go-toml","Apache-2.0" +"github.com/peterhellberg/link","MIT" +"github.com/phayes/freeport","BSD-3-Clause" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/pkg/profile","BSD-2-Clause" +"github.com/pmezard/go-difflib/difflib","BSD-3-Clause" +"github.com/pquerna/otp","Apache-2.0" +"github.com/prometheus/client_golang/prometheus","Apache-2.0" +"github.com/prometheus/client_model/go","Apache-2.0" +"github.com/prometheus/common","Apache-2.0" +"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg","BSD-3-Clause" +"github.com/prometheus/procfs","Apache-2.0" +"github.com/rogpeppe/go-internal/modfile","BSD-3-Clause" +"github.com/rs/cors","MIT" +"github.com/samber/lo","MIT" +"github.com/seatgeek/logrus-gelf-formatter","BSD-3-Clause" +"github.com/segmentio/backo-go","MIT" +"github.com/sergi/go-diff/diffmatchpatch","MIT" +"github.com/shopspring/decimal","MIT" +"github.com/sirupsen/logrus","MIT" +"github.com/slack-go/slack","BSD-2-Clause" +"github.com/sourcegraph/annotate","BSD-3-Clause" +"github.com/sourcegraph/syntaxhighlight","BSD-3-Clause" +"github.com/spf13/cast","MIT" +"github.com/spf13/cobra","Apache-2.0" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/stretchr/testify","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/sjson","MIT" +"github.com/urfave/negroni","MIT" +"github.com/wI2L/jsondiff","MIT" +"github.com/x448/float16","MIT" +"github.com/xeipuuv/gojsonpointer","Apache-2.0" +"github.com/xeipuuv/gojsonreference","Apache-2.0" +"github.com/xeipuuv/gojsonschema","Apache-2.0" +"github.com/xtgo/uuid","BSD-3-Clause" +"github.com/zmb3/spotify/v2","Apache-2.0" +"go.mongodb.org/mongo-driver","Apache-2.0" +"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace","Apache-2.0" +"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp","Apache-2.0" +"go.opentelemetry.io/contrib/propagators/b3","Apache-2.0" +"go.opentelemetry.io/contrib/propagators/jaeger","Apache-2.0" +"go.opentelemetry.io/contrib/samplers/jaegerremote","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/exporters/jaeger","Apache-2.0" +"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift","Apache-2.0" +"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift","GNU-All-permissive-Copying-License" +"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift","BSD-3-Clause" +"go.opentelemetry.io/otel/exporters/otlp/otlptrace","Apache-2.0" +"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp","Apache-2.0" +"go.opentelemetry.io/otel/exporters/zipkin","Apache-2.0" +"go.opentelemetry.io/otel/metric","Apache-2.0" +"go.opentelemetry.io/otel/sdk","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"go.opentelemetry.io/proto/otlp","Apache-2.0" +"golang.org/x/crypto","BSD-3-Clause" +"golang.org/x/exp/slices","BSD-3-Clause" +"golang.org/x/mod","BSD-3-Clause" +"golang.org/x/net","BSD-3-Clause" +"golang.org/x/oauth2","BSD-3-Clause" +"golang.org/x/sync","BSD-3-Clause" +"golang.org/x/sys","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"golang.org/x/xerrors","BSD-3-Clause" +"google.golang.org/genproto/googleapis/api","Apache-2.0" +"google.golang.org/genproto/googleapis/rpc","Apache-2.0" +"google.golang.org/grpc","Apache-2.0" +"google.golang.org/protobuf","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"gopkg.in/yaml.v3","MIT" +"sigs.k8s.io/yaml","MIT" +"sigs.k8s.io/yaml","BSD-3-Clause" +"dario.cat/mergo","BSD-3-Clause" +"github.com/Masterminds/goutils","Apache-2.0" +"github.com/Masterminds/semver/v3","MIT" +"github.com/Masterminds/sprig/v3","MIT" +"github.com/google/uuid","BSD-3-Clause" +"github.com/huandu/xstrings","MIT" +"github.com/imdario/mergo","BSD-3-Clause" +"github.com/mitchellh/copystructure","MIT" +"github.com/mitchellh/reflectwalk","MIT" +"github.com/shopspring/decimal","MIT" +"github.com/spf13/cast","MIT" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/arbovm/levenshtein","BSD-3-Clause" +"github.com/avast/retry-go/v3","MIT" +"github.com/bradleyjkemp/cupaloy/v2","MIT" +"github.com/davecgh/go-spew/spew","ISC" +"github.com/pmezard/go-difflib/difflib","BSD-3-Clause" +"github.com/bwmarrin/discordgo","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/cenkalti/backoff","MIT" +"github.com/bmatcuk/doublestar","MIT" +"github.com/cortesi/modd","MIT" +"github.com/cortesi/modd/conf","BSD-3-Clause" +"github.com/cortesi/moddwatch","MIT" +"github.com/cortesi/termlog","MIT" +"github.com/fatih/color","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"github.com/rjeczalik/notify","MIT" +"golang.org/x/crypto/ssh/terminal","BSD-3-Clause" +"golang.org/x/net/context","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/term","BSD-3-Clause" +"github.com/dghubble/oauth1","MIT" +"github.com/cespare/xxhash/v2","MIT" +"github.com/dgraph-io/ristretto","Apache-2.0" +"github.com/dgraph-io/ristretto/z","MIT" +"github.com/dustin/go-humanize","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/fatih/color","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/ghodss/yaml","MIT" +"github.com/ghodss/yaml","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"github.com/go-crypt/crypt","MIT" +"github.com/go-crypt/x","BSD-3-Clause" +"golang.org/x/sys/cpu","BSD-3-Clause" +"github.com/go-faker/faker/v4","MIT" +"golang.org/x/text","BSD-3-Clause" +"github.com/asaskevich/govalidator","MIT" +"github.com/go-openapi/errors","Apache-2.0" +"github.com/go-openapi/strfmt","Apache-2.0" +"github.com/google/uuid","BSD-3-Clause" +"github.com/mitchellh/mapstructure","MIT" +"github.com/oklog/ulid","Apache-2.0" +"go.mongodb.org/mongo-driver","Apache-2.0" +"github.com/gabriel-vasile/mimetype","MIT" +"github.com/go-playground/locales","MIT" +"github.com/go-playground/universal-translator","MIT" +"github.com/go-playground/validator/v10","MIT" +"github.com/leodido/go-urn","MIT" +"golang.org/x/crypto/sha3","BSD-3-Clause" +"golang.org/x/net/html","BSD-3-Clause" +"golang.org/x/sys/cpu","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"github.com/go-swagger/go-swagger","Apache-2.0" +"github.com/gobuffalo/httptest","MIT" +"github.com/gobuffalo/httptest/internal/takeon/github.com/ajg/form","BSD-3-Clause" +"github.com/gobuffalo/httptest/internal/takeon/github.com/markbates/hmax","MIT" +"filippo.io/edwards25519","BSD-3-Clause" +"github.com/Masterminds/semver/v3","MIT" +"github.com/aymerick/douceur","MIT" +"github.com/fatih/color","MIT" +"github.com/fatih/structs","MIT" +"github.com/go-sql-driver/mysql","MPL-2.0" +"github.com/gobuffalo/envy","MIT" +"github.com/gobuffalo/fizz","MIT" +"github.com/gobuffalo/flect","MIT" +"github.com/gobuffalo/github_flavored_markdown","MIT" +"github.com/gobuffalo/github_flavored_markdown/internal/russross/blackfriday","BSD-2-Clause" +"github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/sanitized_anchor_name","MIT" +"github.com/gobuffalo/helpers","MIT" +"github.com/gobuffalo/nulls","MIT" +"github.com/gobuffalo/plush/v4","MIT" +"github.com/gobuffalo/pop/v6","MIT" +"github.com/gobuffalo/tags/v3","MIT" +"github.com/gobuffalo/validate/v3","MIT" +"github.com/gofrs/uuid","MIT" +"github.com/gorilla/css/scanner","BSD-3-Clause" +"github.com/jackc/chunkreader/v2","MIT" +"github.com/jackc/pgconn","MIT" +"github.com/jackc/pgio","MIT" +"github.com/jackc/pgpassfile","MIT" +"github.com/jackc/pgproto3/v2","MIT" +"github.com/jackc/pgservicefile","MIT" +"github.com/jackc/pgx/v5","MIT" +"github.com/jackc/puddle/v2","MIT" +"github.com/jmoiron/sqlx","MIT" +"github.com/joho/godotenv","MIT" +"github.com/kballard/go-shellquote","MIT" +"github.com/luna-duclos/instrumentedsql","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"github.com/microcosm-cc/bluemonday","BSD-3-Clause" +"github.com/rogpeppe/go-internal/modfile","BSD-3-Clause" +"github.com/sergi/go-diff/diffmatchpatch","MIT" +"github.com/sourcegraph/annotate","BSD-3-Clause" +"github.com/sourcegraph/syntaxhighlight","BSD-3-Clause" +"golang.org/x/crypto/pbkdf2","BSD-3-Clause" +"golang.org/x/mod","BSD-3-Clause" +"golang.org/x/net/html","BSD-3-Clause" +"golang.org/x/sync","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"github.com/gofrs/uuid","MIT" +"github.com/golang-jwt/jwt/v4","MIT" +"github.com/golang-jwt/jwt/v5","MIT" +"github.com/google/go-jsonnet","Apache-2.0" +"gopkg.in/yaml.v2","Apache-2.0" +"sigs.k8s.io/yaml","MIT" +"sigs.k8s.io/yaml","BSD-3-Clause" +"github.com/gorilla/securecookie","BSD-3-Clause" +"github.com/gorilla/sessions","BSD-3-Clause" +"github.com/gtank/cryptopasta","CC0-1.0" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/hashicorp/go-cleanhttp","MPL-2.0" +"github.com/hashicorp/go-retryablehttp","MPL-2.0" +"github.com/hashicorp/golang-lru/v2","MPL-2.0" +"github.com/hashicorp/golang-lru/v2/simplelru","BSD-3-Clause" +"github.com/inhies/go-bytesize","BSD-3-Clause" +"github.com/jarcoal/httpmock","MIT" +"github.com/jmoiron/sqlx","MIT" +"github.com/julienschmidt/httprouter","BSD-3-Clause" +"github.com/knadh/koanf/parsers/json","MIT" +"github.com/lestrrat-go/jwx","MIT" +"github.com/lestrrat-go/option","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/lestrrat-go/jwx/v2","MIT" +"github.com/lestrrat-go/option","MIT" +"github.com/luna-duclos/instrumentedsql","MIT" +"github.com/gorilla/context","BSD-3-Clause" +"github.com/gorilla/mux","BSD-3-Clause" +"github.com/gorilla/pat","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"github.com/ian-kent/envconf","MIT" +"github.com/ian-kent/go-log","MIT" +"github.com/ian-kent/goose","MIT" +"github.com/ian-kent/linkio","Unknown" +"github.com/mailhog/MailHog","MIT" +"github.com/mailhog/MailHog-Server","MIT" +"github.com/mailhog/MailHog-UI","MIT" +"github.com/mailhog/data","MIT" +"github.com/mailhog/http","MIT" +"github.com/mailhog/mhsendmail/cmd","MIT" +"github.com/mailhog/smtp","MIT" +"github.com/mailhog/storage","MIT" +"github.com/ogier/pflag","BSD-3-Clause" +"github.com/philhofer/fwd","MIT" +"github.com/t-k/fluent-logger-golang/fluent","Unknown" +"github.com/tinylib/msgp/msgp","MIT" +"golang.org/x/crypto","BSD-3-Clause" +"gopkg.in/mgo.v2","BSD-2-Clause" +"gopkg.in/mgo.v2/bson","BSD-2-Clause" +"gopkg.in/mgo.v2/internal/json","BSD-3-Clause" +"github.com/mattn/goveralls","MIT" +"golang.org/x/mod","BSD-3-Clause" +"golang.org/x/tools","BSD-3-Clause" +"github.com/mohae/deepcopy","MIT" +"github.com/montanaflynn/stats","MIT" +"github.com/nyaruka/phonenumbers","MIT" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/protobuf","BSD-3-Clause" +"github.com/ory/analytics-go/v5","MIT" +"github.com/segmentio/backo-go","MIT" +"github.com/xtgo/uuid","BSD-3-Clause" +"github.com/ory/client-go","Unknown" +"golang.org/x/oauth2","BSD-3-Clause" +"dario.cat/mergo","BSD-3-Clause" +"github.com/Nvveen/Gotty","BSD-2-Clause" +"github.com/cenkalti/backoff/v4","MIT" +"github.com/containerd/continuity/pathdriver","Apache-2.0" +"github.com/docker/cli","Apache-2.0" +"github.com/docker/docker","Apache-2.0" +"github.com/docker/go-connections/nat","Apache-2.0" +"github.com/docker/go-units","Apache-2.0" +"github.com/gogo/protobuf/proto","BSD-3-Clause" +"github.com/google/shlex","Apache-2.0" +"github.com/mitchellh/mapstructure","MIT" +"github.com/moby/docker-image-spec/specs-go/v1","Apache-2.0" +"github.com/moby/term","Apache-2.0" +"github.com/opencontainers/go-digest","Apache-2.0" +"github.com/opencontainers/image-spec/specs-go","Apache-2.0" +"github.com/opencontainers/runc/libcontainer/user","Apache-2.0" +"github.com/ory/dockertest/v3","Apache-2.0" +"github.com/ory/dockertest/v3/docker","BSD-2-Clause" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/sirupsen/logrus","MIT" +"github.com/xeipuuv/gojsonpointer","Apache-2.0" +"github.com/xeipuuv/gojsonreference","Apache-2.0" +"github.com/xeipuuv/gojsonschema","Apache-2.0" +"golang.org/x/sys/unix","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"github.com/fsnotify/fsnotify","BSD-3-Clause" +"github.com/hashicorp/hcl","MPL-2.0" +"github.com/magiconair/properties","BSD-2-Clause" +"github.com/mitchellh/mapstructure","MIT" +"github.com/ory/go-acc","Apache-2.0" +"github.com/pelletier/go-toml/v2","MIT" +"github.com/sagikazarmark/slog-shim","BSD-3-Clause" +"github.com/spf13/afero","Apache-2.0" +"github.com/spf13/cast","MIT" +"github.com/spf13/cobra","Apache-2.0" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/spf13/viper","MIT" +"github.com/subosito/gotenv","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"gopkg.in/ini.v1","Apache-2.0" +"gopkg.in/yaml.v3","MIT" +"github.com/ory/graceful","Apache-2.0" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/golang/protobuf/proto","BSD-3-Clause" +"github.com/ory/herodot","Apache-2.0" +"github.com/pkg/errors","BSD-2-Clause" +"golang.org/x/net","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/genproto/googleapis/rpc","Apache-2.0" +"google.golang.org/grpc","Apache-2.0" +"google.golang.org/protobuf","BSD-3-Clause" +"github.com/ory/hydra-client-go/v2","Apache-2.0" +"golang.org/x/oauth2","BSD-3-Clause" +"github.com/nyaruka/phonenumbers","MIT" +"github.com/ory/jsonschema/v3","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/protobuf","BSD-3-Clause" +"github.com/ory/mail/v3","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/ory/nosurf","MIT" +"github.com/ory/x","Apache-2.0" +"github.com/peterhellberg/link","MIT" +"github.com/phayes/freeport","BSD-3-Clause" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/boombuler/barcode","MIT" +"github.com/pquerna/otp","Apache-2.0" +"github.com/rs/cors","MIT" +"github.com/samber/lo","MIT" +"golang.org/x/text","BSD-3-Clause" +"github.com/sirupsen/logrus","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"github.com/slack-go/slack","BSD-2-Clause" +"github.com/spf13/cobra","Apache-2.0" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/stretchr/testify","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/sjson","MIT" +"github.com/urfave/negroni","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/sjson","MIT" +"github.com/wI2L/jsondiff","MIT" +"github.com/zmb3/spotify/v2","Apache-2.0" +"golang.org/x/oauth2","BSD-3-Clause" +"github.com/felixge/httpsnoop","MIT" +"github.com/go-logr/logr","Apache-2.0" +"github.com/go-logr/stdr","Apache-2.0" +"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/metric","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"github.com/go-logr/logr","Apache-2.0" +"github.com/go-logr/stdr","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/metric","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"go.opentelemetry.io/otel/sdk","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"golang.org/x/oauth2","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"golang.org/x/net","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/genproto/googleapis/rpc/status","Apache-2.0" +"google.golang.org/grpc","Apache-2.0" +"google.golang.org/protobuf","BSD-3-Clause" + diff --git a/CHANGELOG.md b/CHANGELOG.md index d258cdd832e0..1ae680f41bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-04)](#2024-12-04) +- [ (2024-12-31)](#2024-12-31) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-04) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-31) ## Breaking Changes @@ -366,6 +366,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Add missing autocomplete attributes to identifier_first strategy ([#4215](https://github.com/ory/kratos/issues/4215)) ([e1f29c2](https://github.com/ory/kratos/commit/e1f29c2d3524f9444ec067c52d2c9f1d44fa6539)) +- Cancel conditional passkey before trying again + ([#4247](https://github.com/ory/kratos/issues/4247)) + ([d9f6f75](https://github.com/ory/kratos/commit/d9f6f75b6a43aad996f6390f73616a2cf596c6e4)) - Do not roll back transaction on partial identity insert error ([#4211](https://github.com/ory/kratos/issues/4211)) ([82660f0](https://github.com/ory/kratos/commit/82660f04e2f33d0aa86fccee42c90773a901d400)) @@ -391,9 +394,15 @@ https://github.com/ory-corp/cloud/issues/7176 - Pass on correct context during verification ([#4151](https://github.com/ory/kratos/issues/4151)) ([7e0b500](https://github.com/ory/kratos/commit/7e0b500aada9c1931c759a43db7360e85afb57e3)) +- Preview_credentials_identifier_similar + ([#4246](https://github.com/ory/kratos/issues/4246)) + ([5ee54ed](https://github.com/ory/kratos/commit/5ee54eda909638fa10c543f156042a217b34cba6)) - Registration post persist hooks should not be cancelable ([#4148](https://github.com/ory/kratos/issues/4148)) ([18056a0](https://github.com/ory/kratos/commit/18056a0f1cfdf42769e5a974b2526ccf5c608cc2)) +- **sdk:** Add missing captcha group + ([#4254](https://github.com/ory/kratos/issues/4254)) + ([241111b](https://github.com/ory/kratos/commit/241111b21f5d96b26ff8bc8106dc8a527c68063b)) - **sdk:** Remove incorrect attributes ([#4163](https://github.com/ory/kratos/issues/4163)) ([88c68aa](https://github.com/ory/kratos/commit/88c68aa07281a638c9897e76d300d1095b17601d)) @@ -538,6 +547,13 @@ https://github.com/ory-corp/cloud/issues/7176 - Improve secondary indices for self service tables ([#4179](https://github.com/ory/kratos/issues/4179)) ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253)) +- Improved tracing for courier + ([85a7071](https://github.com/ory/kratos/commit/85a7071d20d0f072316c74bee82c76ee690276f8)) +- Jackson provider ([#4242](https://github.com/ory/kratos/issues/4242)) + ([f18d1b2](https://github.com/ory/kratos/commit/f18d1b24539f7d8dcf9c27986af861d0f8cb9683)): + + This adds a jackson provider to Kratos. + - Load session only once when middleware is used ([#4187](https://github.com/ory/kratos/issues/4187)) ([234b6f2](https://github.com/ory/kratos/commit/234b6f2f6435c62b7e161c032b888c4e2b3328d4)) @@ -571,6 +587,23 @@ https://github.com/ory-corp/cloud/issues/7176 - Remove more unused indices ([#4186](https://github.com/ory/kratos/issues/4186)) ([b294804](https://github.com/ory/kratos/commit/b2948044de4eee1841110162fe874055182bd2d2)) +- Rework the OTP code submit count mechanism + ([#4251](https://github.com/ory/kratos/issues/4251)) + ([4ca4d79](https://github.com/ory/kratos/commit/4ca4d79cff5185caad27eddee7e6f8d0e58463ba)): + + - feat: rework the OTP code submit count mechanism + + Unlike what the previous comment suggested, incrementing and checking the + submit count inside the database transaction is not actually optimal + peformance- or security-wise. + + We now check atomically increment and check the submit count as the first part + of the operation, and abort as early as possible if we detect brute-forcing. + This prevents a situation where the check works only on certain transaction + isolation levels. + + - chore: bump dependencies + - Support android webauthn origins ([#4155](https://github.com/ory/kratos/issues/4155)) ([a82d288](https://github.com/ory/kratos/commit/a82d288014411ae4eb82c718bfe825ca55b4fab0)): diff --git a/courier/courier_dispatcher.go b/courier/courier_dispatcher.go index 75745ef9a954..8d7a5773c5ab 100644 --- a/courier/courier_dispatcher.go +++ b/courier/courier_dispatcher.go @@ -7,6 +7,10 @@ import ( "context" "github.com/pkg/errors" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + "github.com/ory/x/otelx" ) func (c *courier) channels(ctx context.Context, id string) (Channel, error) { @@ -36,7 +40,16 @@ func (c *courier) channels(ctx context.Context, id string) (Channel, error) { return nil, errors.Errorf("no courier channels configured") } -func (c *courier) DispatchMessage(ctx context.Context, msg Message) error { +func (c *courier) DispatchMessage(ctx context.Context, msg Message) (err error) { + ctx, span := c.deps.Tracer(ctx).Tracer().Start(ctx, "courier.DispatchMessage", trace.WithAttributes( + attribute.Stringer("message.id", msg.ID), + attribute.Stringer("message.nid", msg.NID), + attribute.Stringer("message.type", msg.Type), + attribute.String("message.template_type", string(msg.TemplateType)), + attribute.Int("message.send_count", msg.SendCount), + )) + defer otelx.End(span, &err) + logger := c.deps.Logger(). WithField("message_id", msg.ID). WithField("message_nid", msg.NID). @@ -56,6 +69,7 @@ func (c *courier) DispatchMessage(ctx context.Context, msg Message) error { return err } + span.SetAttributes(attribute.String("channel.id", channel.ID())) logger = logger. WithField("channel", channel.ID()) diff --git a/courier/smtp_channel.go b/courier/smtp_channel.go index 15a685bcd7ad..3527b2b7d680 100644 --- a/courier/smtp_channel.go +++ b/courier/smtp_channel.go @@ -5,15 +5,19 @@ package courier import ( "context" - "fmt" + "net" "net/textproto" + "strconv" "github.com/pkg/errors" + semconv "go.opentelemetry.io/otel/semconv/v1.20.0" + "go.opentelemetry.io/otel/trace" "github.com/ory/herodot" "github.com/ory/kratos/courier/template" "github.com/ory/kratos/driver/config" "github.com/ory/mail/v3" + "github.com/ory/x/otelx" ) type ( @@ -47,7 +51,10 @@ func (c *SMTPChannel) ID() string { return "email" } -func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) error { +func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) (err error) { + ctx, span := c.d.Tracer(ctx).Tracer().Start(ctx, "courier.SMTPChannel.Dispatch") + defer otelx.End(span, &err) + if c.smtpClient.Host == "" { return errors.WithStack(herodot.ErrInternalServerError.WithErrorf("Courier tried to deliver an email but %s is not set!", config.ViperKeyCourierSMTPURL)) } @@ -87,7 +94,7 @@ func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) error { gm.SetBody("text/plain", msg.Body) logger := c.d.Logger(). - WithField("smtp_server", fmt.Sprintf("%s:%d", c.smtpClient.Host, c.smtpClient.Port)). + WithField("smtp_server", net.JoinHostPort(c.smtpClient.Host, strconv.Itoa(c.smtpClient.Port))). WithField("smtp_ssl_enabled", c.smtpClient.SSL). WithField("message_from", cfg.FromAddress). WithField("message_id", msg.ID). @@ -107,7 +114,28 @@ func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) error { gm.AddAlternative("text/html", htmlBody) } - if err := errors.WithStack(c.smtpClient.DialAndSend(ctx, gm)); err != nil { + dialCtx, dialSpan := c.d.Tracer(ctx).Tracer().Start(ctx, "courier.SMTPChannel.Dispatch.Dial", trace.WithAttributes( + semconv.NetPeerName(c.smtpClient.Host), + semconv.NetPeerPort(c.smtpClient.Port), + semconv.NetProtocolName("smtp"), + )) + snd, err := c.smtpClient.Dial(dialCtx) + otelx.End(dialSpan, &err) + + if err != nil { + logger. + WithError(err). + Error("Unable to dial SMTP connection.") + return errors.WithStack(herodot.ErrInternalServerError. + WithError(err.Error()).WithReason("failed to send email via smtp")) + } + defer snd.Close() + + sendCtx, sendSpan := c.d.Tracer(ctx).Tracer().Start(ctx, "courier.SMTPChannel.Dispatch.Send") + err = mail.Send(sendCtx, snd, gm) + otelx.End(sendSpan, &err) + + if err != nil { logger. WithError(err). Error("Unable to send email using SMTP connection.") diff --git a/driver/config/config.go b/driver/config/config.go index b1e16e393f13..366f9e37150d 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -193,6 +193,7 @@ const ( ViperKeyIgnoreNetworkErrors = "selfservice.methods.password.config.ignore_network_errors" ViperKeyTOTPIssuer = "selfservice.methods.totp.config.issuer" ViperKeyOIDCBaseRedirectURL = "selfservice.methods.oidc.config.base_redirect_uri" + ViperKeySAMLBaseRedirectURL = "selfservice.methods.saml.config.base_redirect_uri" ViperKeyWebAuthnRPDisplayName = "selfservice.methods.webauthn.config.rp.display_name" ViperKeyWebAuthnRPID = "selfservice.methods.webauthn.config.rp.id" ViperKeyWebAuthnRPOrigin = "selfservice.methods.webauthn.config.rp.origin" @@ -616,6 +617,10 @@ func (p *Config) OIDCRedirectURIBase(ctx context.Context) *url.URL { return p.GetProvider(ctx).URIF(ViperKeyOIDCBaseRedirectURL, p.SelfPublicURL(ctx)) } +func (p *Config) SAMLRedirectURIBase(ctx context.Context) *url.URL { + return p.GetProvider(ctx).URIF(ViperKeySAMLBaseRedirectURL, p.SelfPublicURL(ctx)) +} + func (p *Config) IdentityTraitsSchemas(ctx context.Context) (ss Schemas, err error) { if err = p.GetProvider(ctx).Koanf.Unmarshal(ViperKeyIdentitySchemas, &ss); err != nil { return ss, nil diff --git a/embedx/embedx.go b/embedx/embedx.go index b91d86b8f692..5212337f4ea2 100644 --- a/embedx/embedx.go +++ b/embedx/embedx.go @@ -5,15 +5,13 @@ package embedx import ( "bytes" + _ "embed" "io" "github.com/pkg/errors" - - "github.com/ory/x/otelx" - "github.com/tidwall/gjson" - _ "embed" + "github.com/ory/x/otelx" ) //go:embed config.schema.json diff --git a/go.mod b/go.mod index acdf909951c1..deba6fbdf766 100644 --- a/go.mod +++ b/go.mod @@ -97,12 +97,12 @@ require ( go.opentelemetry.io/otel v1.32.0 go.opentelemetry.io/otel/sdk v1.32.0 go.opentelemetry.io/otel/trace v1.32.0 - golang.org/x/crypto v0.29.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/net v0.31.0 + golang.org/x/crypto v0.31.0 + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect + golang.org/x/net v0.33.0 golang.org/x/oauth2 v0.24.0 - golang.org/x/sync v0.9.0 - golang.org/x/text v0.20.0 + golang.org/x/sync v0.10.0 + golang.org/x/text v0.21.0 google.golang.org/grpc v1.67.1 ) @@ -118,7 +118,8 @@ require ( github.com/dgraph-io/ristretto/v2 v2.0.0 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/rjeczalik/notify v0.9.3 // indirect - golang.org/x/term v0.26.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/time v0.8.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect mvdan.cc/sh/v3 v3.6.0 // indirect ) @@ -257,7 +258,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/term v0.5.0 // indirect - github.com/nyaruka/phonenumbers v1.3.6 + github.com/nyaruka/phonenumbers v1.4.1 github.com/ogier/pflag v0.0.1 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -312,10 +313,10 @@ require ( go.opentelemetry.io/otel/metric v1.32.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/tools v0.23.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/tools v0.28.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/protobuf v1.35.1 diff --git a/go.sum b/go.sum index d48ddcf554f0..5f0133b6a64f 100644 --- a/go.sum +++ b/go.sum @@ -594,8 +594,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nyaruka/phonenumbers v1.3.6 h1:33owXWp4d1U+Tyaj9fpci6PbvaQZcXBUO2FybeKeLwQ= -github.com/nyaruka/phonenumbers v1.3.6/go.mod h1:Ut+eFwikULbmCenH6InMKL9csUNLyxHuBLyfkpum11s= +github.com/nyaruka/phonenumbers v1.4.1 h1:dNsiYGirahC2lMRz3p2dxmmyLbzD3arCgmj/hPEVRPY= +github.com/nyaruka/phonenumbers v1.4.1/go.mod h1:gv+CtldaFz+G3vHHnasBSirAi3O2XLqZzVWz4V1pl2E= github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= @@ -866,8 +866,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4 golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -878,8 +878,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -904,8 +904,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -954,8 +954,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -981,8 +981,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1047,8 +1047,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1060,8 +1060,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1074,13 +1074,13 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1128,14 +1128,14 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/identity/credentials.go b/identity/credentials.go index 9fc2d93851bb..9f3865006f96 100644 --- a/identity/credentials.go +++ b/identity/credentials.go @@ -89,6 +89,7 @@ const ( CredentialsTypeCodeAuth CredentialsType = "code" CredentialsTypePasskey CredentialsType = "passkey" CredentialsTypeProfile CredentialsType = "profile" + CredentialsTypeSAML CredentialsType = "saml" ) func (c CredentialsType) String() string { @@ -99,7 +100,7 @@ func (c CredentialsType) ToUiNodeGroup() node.UiNodeGroup { switch c { case CredentialsTypePassword: return node.PasswordGroup - case CredentialsTypeOIDC: + case CredentialsTypeOIDC, CredentialsTypeSAML: return node.OpenIDConnectGroup case CredentialsTypeTOTP: return node.TOTPGroup @@ -138,6 +139,7 @@ func ParseCredentialsType(in string) (CredentialsType, bool) { for _, t := range []CredentialsType{ CredentialsTypePassword, CredentialsTypeOIDC, + CredentialsTypeSAML, CredentialsTypeTOTP, CredentialsTypeLookup, CredentialsTypeWebAuthn, diff --git a/identity/extension_verification.go b/identity/extension_verification.go index 3b3f92581c37..677a49934bdd 100644 --- a/identity/extension_verification.go +++ b/identity/extension_verification.go @@ -5,12 +5,12 @@ package identity import ( "fmt" + "maps" + "slices" "strings" "sync" "time" - "golang.org/x/exp/maps" - "github.com/ory/jsonschema/v3" "github.com/ory/kratos/schema" ) @@ -60,7 +60,7 @@ func (r *SchemaExtensionVerification) Run(ctx jsonschema.ValidationContext, s sc formatString = "email" formatter, ok := jsonschema.Formats[formatString] if !ok { - supportedKeys := maps.Keys(jsonschema.Formats) + supportedKeys := slices.Collect(maps.Keys(jsonschema.Formats)) return ctx.Error("format", "format %q is not supported. Supported formats are [%s]", formatString, strings.Join(supportedKeys, ", ")) } diff --git a/identity/handler.go b/identity/handler.go index 7560724899db..6d458636854d 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -119,10 +119,7 @@ func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) { // Paginated Identity List Response // // swagger:response listIdentities -// -//nolint:deadcode,unused -//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions -type listIdentitiesResponse struct { +type _ struct { migrationpagination.ResponseHeaderAnnotation // List of identities @@ -133,15 +130,19 @@ type listIdentitiesResponse struct { // Paginated List Identity Parameters // -// swagger:parameters listIdentities +// Note: Filters cannot be combined. // -//nolint:deadcode,unused -//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions -type listIdentitiesParameters struct { +// swagger:parameters listIdentities +type _ struct { migrationpagination.RequestParameters - // List of ids used to filter identities. - // If this list is empty, then no filter will be applied. + // 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). // // required: false // in: query @@ -173,9 +174,8 @@ type listIdentitiesParameters struct { // in: query DeclassifyCredentials []string `json:"include_credential"` - // OrganizationID is the organization id to filter identities by. + // List identities that belong to a specific organization. // - // If `ids` is set, this parameter is ignored. // required: false // in: query OrganizationID string `json:"organization_id"` @@ -183,11 +183,73 @@ type listIdentitiesParameters struct { crdbx.ConsistencyRequestParameters } +func parseListIdentitiesParameters(r *http.Request) (params ListIdentityParameters, err error) { + query := r.URL.Query() + var requestedFilters int + + params.Expand = ExpandDefault + + if ids := query["ids"]; len(ids) > 0 { + requestedFilters++ + for _, v := range ids { + id, err := uuid.FromString(v) + if err != nil { + return params, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `ids`.", v)) + } + params.IdsFilter = append(params.IdsFilter, id) + } + } + if len(params.IdsFilter) > 500 { + return params, errors.WithStack(herodot.ErrBadRequest.WithReason("The number of ids to filter must not exceed 500.")) + } + + if orgID := query.Get("organization_id"); orgID != "" { + requestedFilters++ + params.OrganizationID, err = uuid.FromString(orgID) + if err != nil { + return params, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `organization_id`.", orgID)) + } + } + + if identifier := query.Get("credentials_identifier"); identifier != "" { + requestedFilters++ + params.Expand = ExpandEverything + params.CredentialsIdentifier = identifier + } + + if identifier := query.Get("preview_credentials_identifier_similar"); identifier != "" { + requestedFilters++ + params.Expand = ExpandEverything + params.CredentialsIdentifierSimilar = identifier + } + + for _, v := range query["include_credential"] { + params.Expand = ExpandEverything + tc, ok := ParseCredentialsType(v) + if !ok { + return params, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid value `%s` for parameter `include_credential`.", v)) + } + params.DeclassifyCredentials = append(params.DeclassifyCredentials, tc) + } + + if requestedFilters > 1 { + return params, errors.WithStack(herodot.ErrBadRequest.WithReason("You cannot combine multiple filters in this API")) + } + + params.KeySetPagination, params.PagePagination, err = x.ParseKeysetOrPagePagination(r) + if err != nil { + return params, err + } + params.ConsistencyLevel = crdbx.ConsistencyLevelFromRequest(r) + + return params, nil +} + // swagger:route GET /admin/identities identity listIdentities // // # List Identities // -// Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. +// Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. // // Produces: // - application/json @@ -201,54 +263,7 @@ type listIdentitiesParameters struct { // 200: listIdentities // default: errorGeneric func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - includeCredentials := r.URL.Query()["include_credential"] - var err error - var declassify []CredentialsType - for _, v := range includeCredentials { - tc, ok := ParseCredentialsType(v) - if ok { - declassify = append(declassify, tc) - } else { - h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid value `%s` for parameter `include_credential`.", declassify))) - return - } - } - - var orgId uuid.UUID - if orgIdStr := r.URL.Query().Get("organization_id"); orgIdStr != "" { - orgId, err = uuid.FromString(r.URL.Query().Get("organization_id")) - if err != nil { - h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `organization_id`.", r.URL.Query().Get("organization_id")))) - return - } - } - var idsFilter []uuid.UUID - for _, v := range r.URL.Query()["ids"] { - id, err := uuid.FromString(v) - if err != nil { - h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `ids`.", v))) - return - } - idsFilter = append(idsFilter, id) - } - - params := ListIdentityParameters{ - Expand: ExpandDefault, - IdsFilter: idsFilter, - CredentialsIdentifier: r.URL.Query().Get("credentials_identifier"), - CredentialsIdentifierSimilar: r.URL.Query().Get("preview_credentials_identifier_similar"), - OrganizationID: orgId, - ConsistencyLevel: crdbx.ConsistencyLevelFromRequest(r), - DeclassifyCredentials: declassify, - } - if params.CredentialsIdentifier != "" && params.CredentialsIdentifierSimilar != "" { - h.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithReason("Cannot pass both credentials_identifier and preview_credentials_identifier_similar.")) - return - } - if params.CredentialsIdentifier != "" || params.CredentialsIdentifierSimilar != "" || len(params.DeclassifyCredentials) > 0 { - params.Expand = ExpandEverything - } - params.KeySetPagination, params.PagePagination, err = x.ParseKeysetOrPagePagination(r) + params, err := parseListIdentitiesParameters(r) if err != nil { h.r.Writer().WriteError(w, r, err) return @@ -271,7 +286,7 @@ func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Para } u := *r.URL pagepagination.PaginationHeader(w, &u, total, params.PagePagination.Page, params.PagePagination.ItemsPerPage) - } else { + } else if nextPage != nil { u := *r.URL keysetpagination.Header(w, &u, nextPage) } diff --git a/identity/handler_test.go b/identity/handler_test.go index e3362a6ecf94..66f4936961f3 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -369,21 +369,50 @@ func TestHandler(t *testing.T) { id := x.ParseUUID(res.Get("id").String()) ids = append(ids, id) } - require.Equal(t, len(ids), identitiesAmount) + require.Len(t, ids, identitiesAmount) }) t.Run("case=list few identities", func(t *testing.T) { - url := "/identities?ids=" + ids[0].String() + url := "/identities?ids=" + ids[0].String() + "&ids=" + ids[0].String() // duplicate ID is deduplicated in result for i := 1; i < listAmount; i++ { url += "&ids=" + ids[i].String() } res := get(t, adminTS, url, http.StatusOK) identities := res.Array() - require.Equal(t, len(identities), listAmount) + require.Len(t, identities, listAmount) }) }) + t.Run("case=list identities by ID is capped at 500", func(t *testing.T) { + url := "/identities?ids=" + x.NewUUID().String() + for i := 0; i < 501; i++ { + url += "&ids=" + x.NewUUID().String() + } + res := get(t, adminTS, url, http.StatusBadRequest) + assert.Contains(t, res.Get("error.reason").String(), "must not exceed 500") + }) + + t.Run("case=list identities cannot combine filters", func(t *testing.T) { + filters := []string{ + "ids=" + x.NewUUID().String(), + "credentials_identifier=foo@bar.com", + "preview_credentials_identifier_similar=bar.com", + "organization_id=" + x.NewUUID().String(), + } + for i := range filters { + for j := range filters { + if i == j { + continue // OK to use the same filter multiple times. Behavior varies by filter, though. + } + + url := "/identities?" + filters[i] + "&" + filters[j] + res := get(t, adminTS, url, http.StatusBadRequest) + assert.Contains(t, res.Get("error.reason").String(), "cannot combine multiple filters") + } + } + }) + t.Run("case=malformed ids should return an error", func(t *testing.T) { res := get(t, adminTS, "/identities?ids=not-a-uuid", http.StatusBadRequest) assert.Contains(t, res.Get("error.reason").String(), "Invalid UUID value `not-a-uuid` for parameter `ids`.", "%s", res.Raw) diff --git a/internal/client-go/api_identity.go b/internal/client-go/api_identity.go index 9e4aec1b6c58..2daa8d8d4971 100644 --- a/internal/client-go/api_identity.go +++ b/internal/client-go/api_identity.go @@ -114,7 +114,7 @@ type IdentityAPI interface { You cannot delete password or code auth credentials through this API. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @param id ID is the identity's ID. - * @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 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 + * @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 * @return IdentityAPIApiDeleteIdentityCredentialsRequest */ DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest @@ -227,7 +227,7 @@ type IdentityAPI interface { /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ @@ -1090,7 +1090,7 @@ func (r IdentityAPIApiDeleteIdentityCredentialsRequest) Execute() (*http.Respons You cannot delete password or code auth credentials through this API. - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID is the identity's ID. - - @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 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 + - @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 - @return IdentityAPIApiDeleteIdentityCredentialsRequest */ func (a *IdentityAPIService) DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest { @@ -2137,7 +2137,7 @@ func (r IdentityAPIApiListIdentitiesRequest) Execute() ([]Identity, *http.Respon /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ diff --git a/internal/client-go/model_identity_credentials.go b/internal/client-go/model_identity_credentials.go index 7ee96800df4b..de087e64e09f 100644 --- a/internal/client-go/model_identity_credentials.go +++ b/internal/client-go/model_identity_credentials.go @@ -23,7 +23,7 @@ type IdentityCredentials struct { CreatedAt *time.Time `json:"created_at,omitempty"` // Identifiers represents a list of unique identifiers this credential type matches. Identifiers []string `json:"identifiers,omitempty"` - // Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile 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 + // 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 Type *string `json:"type,omitempty"` // UpdatedAt is a helper struct field for gobuffalo.pop. UpdatedAt *time.Time `json:"updated_at,omitempty"` diff --git a/internal/client-go/model_login_flow.go b/internal/client-go/model_login_flow.go index 2794adee0b83..5fc35379ea48 100644 --- a/internal/client-go/model_login_flow.go +++ b/internal/client-go/model_login_flow.go @@ -18,7 +18,7 @@ import ( // 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. type LoginFlow struct { - // 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 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 + // 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 Active *string `json:"active,omitempty"` // CreatedAt is a helper struct field for gobuffalo.pop. CreatedAt *time.Time `json:"created_at,omitempty"` diff --git a/internal/client-go/model_registration_flow.go b/internal/client-go/model_registration_flow.go index c0ba64843d3f..4eb2d78f6052 100644 --- a/internal/client-go/model_registration_flow.go +++ b/internal/client-go/model_registration_flow.go @@ -18,7 +18,7 @@ import ( // RegistrationFlow struct for RegistrationFlow type RegistrationFlow struct { - // 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 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 + // 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 Active *string `json:"active,omitempty"` // ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated. ExpiresAt time.Time `json:"expires_at"` diff --git a/internal/client-go/model_ui_node.go b/internal/client-go/model_ui_node.go index 3582d9e85f67..84b728514114 100644 --- a/internal/client-go/model_ui_node.go +++ b/internal/client-go/model_ui_node.go @@ -18,7 +18,7 @@ import ( // UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `` tag, or an `` but also `some plain text`. type UiNode struct { Attributes UiNodeAttributes `json:"attributes"` - // 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 + // 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 Group string `json:"group"` Messages []UiText `json:"messages"` Meta UiNodeMeta `json:"meta"` diff --git a/internal/httpclient/api_identity.go b/internal/httpclient/api_identity.go index 9e4aec1b6c58..2daa8d8d4971 100644 --- a/internal/httpclient/api_identity.go +++ b/internal/httpclient/api_identity.go @@ -114,7 +114,7 @@ type IdentityAPI interface { You cannot delete password or code auth credentials through this API. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @param id ID is the identity's ID. - * @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 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 + * @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 * @return IdentityAPIApiDeleteIdentityCredentialsRequest */ DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest @@ -227,7 +227,7 @@ type IdentityAPI interface { /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ @@ -1090,7 +1090,7 @@ func (r IdentityAPIApiDeleteIdentityCredentialsRequest) Execute() (*http.Respons You cannot delete password or code auth credentials through this API. - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID is the identity's ID. - - @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 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 + - @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 - @return IdentityAPIApiDeleteIdentityCredentialsRequest */ func (a *IdentityAPIService) DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest { @@ -2137,7 +2137,7 @@ func (r IdentityAPIApiListIdentitiesRequest) Execute() ([]Identity, *http.Respon /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ diff --git a/internal/httpclient/model_identity_credentials.go b/internal/httpclient/model_identity_credentials.go index 7ee96800df4b..de087e64e09f 100644 --- a/internal/httpclient/model_identity_credentials.go +++ b/internal/httpclient/model_identity_credentials.go @@ -23,7 +23,7 @@ type IdentityCredentials struct { CreatedAt *time.Time `json:"created_at,omitempty"` // Identifiers represents a list of unique identifiers this credential type matches. Identifiers []string `json:"identifiers,omitempty"` - // Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile 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 + // 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 Type *string `json:"type,omitempty"` // UpdatedAt is a helper struct field for gobuffalo.pop. UpdatedAt *time.Time `json:"updated_at,omitempty"` diff --git a/internal/httpclient/model_login_flow.go b/internal/httpclient/model_login_flow.go index 2794adee0b83..5fc35379ea48 100644 --- a/internal/httpclient/model_login_flow.go +++ b/internal/httpclient/model_login_flow.go @@ -18,7 +18,7 @@ import ( // 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. type LoginFlow struct { - // 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 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 + // 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 Active *string `json:"active,omitempty"` // CreatedAt is a helper struct field for gobuffalo.pop. CreatedAt *time.Time `json:"created_at,omitempty"` diff --git a/internal/httpclient/model_registration_flow.go b/internal/httpclient/model_registration_flow.go index c0ba64843d3f..4eb2d78f6052 100644 --- a/internal/httpclient/model_registration_flow.go +++ b/internal/httpclient/model_registration_flow.go @@ -18,7 +18,7 @@ import ( // RegistrationFlow struct for RegistrationFlow type RegistrationFlow struct { - // 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 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 + // 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 Active *string `json:"active,omitempty"` // ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated. ExpiresAt time.Time `json:"expires_at"` diff --git a/internal/httpclient/model_ui_node.go b/internal/httpclient/model_ui_node.go index 3582d9e85f67..84b728514114 100644 --- a/internal/httpclient/model_ui_node.go +++ b/internal/httpclient/model_ui_node.go @@ -18,7 +18,7 @@ import ( // UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `` tag, or an `` but also `some plain text`. type UiNode struct { Attributes UiNodeAttributes `json:"attributes"` - // 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 + // 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 Group string `json:"group"` Messages []UiText `json:"messages"` Meta UiNodeMeta `json:"meta"` diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 8d5a08f04415..990f97a550ba 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -1337,7 +1337,7 @@ func FindIdentityCredentialsTypeByName(con *pop.Connection, ct identity.Credenti } if !found { - return uuid.Nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The SQL adapter failed to return the appropriate credentials_type for nane %s. This is a bug in the code.", ct)) + return 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)) } return result, nil diff --git a/persistence/sql/persister_code.go b/persistence/sql/persister_code.go index ece7dea75ec3..8b859918e389 100644 --- a/persistence/sql/persister_code.go +++ b/persistence/sql/persister_code.go @@ -12,6 +12,8 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" "github.com/pkg/errors" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/selfservice/strategy/code" "github.com/ory/x/otelx" @@ -41,7 +43,7 @@ func useOneTimeCode[P any, U interface { *P oneTimeCodeProvider }](ctx context.Context, p *Persister, flowID uuid.UUID, userProvidedCode string, flowTableName string, foreignKeyName string, opts ...codeOption, -) (_ U, err error) { +) (target U, err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.useOneTimeCode") defer otelx.End(span, &err) @@ -50,33 +52,21 @@ func useOneTimeCode[P any, U interface { opt(o) } - var target U - nid := p.NetworkID(ctx) - if err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { - //#nosec G201 -- TableName is static - if err := tx.RawQuery(fmt.Sprintf("UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ?", flowTableName), flowID, nid).Exec(); err != nil { - return err - } - - var submitCount int - // Because MySQL does not support "RETURNING" clauses, but we need the updated `submit_count` later on. - //#nosec G201 -- TableName is static - if err := sqlcon.HandleError(tx.RawQuery(fmt.Sprintf("SELECT submit_count FROM %s WHERE id = ? AND nid = ?", flowTableName), flowID, nid).First(&submitCount)); err != nil { - if errors.Is(err, sqlcon.ErrNoRows) { - // Return no error, as that would roll back the transaction - return nil - } - return err - } + // Before we do anything else, increment the submit count and check if we're + // being brute-forced. This is a separate statement/transaction to the rest + // of the operations so that it is correct for all transaction isolation + // levels. + submitCount, err := incrementOTPCodeSubmitCount(ctx, p, flowID, flowTableName) + if err != nil { + return nil, err + } + if submitCount > 5 { + return nil, errors.WithStack(code.ErrCodeSubmittedTooOften) + } - // This check prevents parallel brute force attacks by checking the submit count inside this database - // transaction. If the flow has been submitted more than 5 times, the transaction is aborted (regardless of - // whether the code was correct or not) and we thus give no indication whether the supplied code was correct or - // not. For more explanation see [this comment](https://github.com/ory/kratos/pull/2645#discussion_r984732899). - if submitCount > 5 { - return errors.WithStack(code.ErrCodeSubmittedTooOften) - } + nid := p.NetworkID(ctx) + if err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { var codes []U codesQuery := tx.Where(fmt.Sprintf("nid = ? AND %s = ?", foreignKeyName), nid, flowID) if o.IdentityID != nil { @@ -85,10 +75,8 @@ func useOneTimeCode[P any, U interface { if err := sqlcon.HandleError(codesQuery.All(&codes)); err != nil { if errors.Is(err, sqlcon.ErrNoRows) { - // Return no error, as that would roll back the transaction and reset the submit count. - return nil + return errors.WithStack(code.ErrCodeNotFound) } - return err } @@ -107,7 +95,7 @@ func useOneTimeCode[P any, U interface { } if target.Validate() != nil { - // Return no error, as that would roll back the transaction + // Return no error, as that would roll back the transaction. We re-validate the code after the transaction. return nil } @@ -123,3 +111,52 @@ func useOneTimeCode[P any, U interface { return target, nil } + +func incrementOTPCodeSubmitCount(ctx context.Context, p *Persister, flowID uuid.UUID, flowTableName string) (submitCount int, err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.incrementOTPCodeSubmitCount", + trace.WithAttributes(attribute.Stringer("flow_id", flowID), attribute.String("flow_table_name", flowTableName))) + defer otelx.End(span, &err) + defer func() { + span.SetAttributes(attribute.Int("submit_count", submitCount)) + }() + + nid := p.NetworkID(ctx) + + // The branch below is a marginal performance optimization for databases + // supporting RETURNING (one query instead of two). There is no real + // security difference here, but there is an observable difference in + // behavior. + // + // Databases supporting RETURNING will perform the increment+select + // atomically. That means that always exactly 5 attempts will be allowed for + // each flow, no matter how many concurrent attempts are made. + // + // Databases without support for RETURNING (MySQL) will perform the UPDATE + // and SELECT in two queries, which are not atomic. The effect is that there + // will still never be more than 5 attempts for each flow, but there may be + // fewer before we reject. Under normal operation, this is never a problem + // because a human will never submit their code as quickly as would be + // required to trigger this race condition. + // + // In a very strict sense of the word, the MySQL implementation is even more + // secure than the RETURNING implementation. But we're ok either way :) + if p.c.Dialect.Name() == "mysql" { + //#nosec G201 -- TableName is static + qUpdate := fmt.Sprintf("UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ?", flowTableName) + if err := p.GetConnection(ctx).RawQuery(qUpdate, flowID, nid).Exec(); err != nil { + return 0, sqlcon.HandleError(err) + } + //#nosec G201 -- TableName is static + qSelect := fmt.Sprintf("SELECT submit_count FROM %s WHERE id = ? AND nid = ?", flowTableName) + err = sqlcon.HandleError(p.GetConnection(ctx).RawQuery(qSelect, flowID, nid).First(&submitCount)) + } else { + //#nosec G201 -- TableName is static + q := fmt.Sprintf("UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ? RETURNING submit_count", flowTableName) + err = sqlcon.HandleError(p.Connection(ctx).RawQuery(q, flowID, nid).First(&submitCount)) + } + if errors.Is(err, sqlcon.ErrNoRows) { + return 0, errors.WithStack(code.ErrCodeNotFound) + } + + return submitCount, err +} diff --git a/persistence/sql/persister_login_code.go b/persistence/sql/persister_login_code.go index 808e65b9d2a4..deee50f02f59 100644 --- a/persistence/sql/persister_login_code.go +++ b/persistence/sql/persister_login_code.go @@ -43,7 +43,7 @@ func (p *Persister) UseLoginCode(ctx context.Context, flowID uuid.UUID, identity ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseLoginCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.LoginCode, *code.LoginCode](ctx, p, flowID, userProvidedCode, new(login.Flow).TableName(ctx), "selfservice_login_flow_id", withCheckIdentityID(identityID)) + codeRow, err := useOneTimeCode[code.LoginCode](ctx, p, flowID, userProvidedCode, new(login.Flow).TableName(ctx), "selfservice_login_flow_id", withCheckIdentityID(identityID)) if err != nil { return nil, err } diff --git a/persistence/sql/persister_recovery_code.go b/persistence/sql/persister_recovery_code.go index 9dc4dd26bb83..7b27aff12e84 100644 --- a/persistence/sql/persister_recovery_code.go +++ b/persistence/sql/persister_recovery_code.go @@ -58,7 +58,7 @@ func (p *Persister) UseRecoveryCode(ctx context.Context, flowID uuid.UUID, userP ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseRecoveryCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.RecoveryCode, *code.RecoveryCode](ctx, p, flowID, userProvidedCode, new(recovery.Flow).TableName(ctx), "selfservice_recovery_flow_id") + codeRow, err := useOneTimeCode[code.RecoveryCode](ctx, p, flowID, userProvidedCode, new(recovery.Flow).TableName(ctx), "selfservice_recovery_flow_id") if err != nil { return nil, err } diff --git a/persistence/sql/persister_registration_code.go b/persistence/sql/persister_registration_code.go index 095cb45156ba..3ef33048a60e 100644 --- a/persistence/sql/persister_registration_code.go +++ b/persistence/sql/persister_registration_code.go @@ -44,7 +44,7 @@ func (p *Persister) UseRegistrationCode(ctx context.Context, flowID uuid.UUID, u ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseRegistrationCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.RegistrationCode, *code.RegistrationCode](ctx, p, flowID, userProvidedCode, new(registration.Flow).TableName(ctx), "selfservice_registration_flow_id") + codeRow, err := useOneTimeCode[code.RegistrationCode](ctx, p, flowID, userProvidedCode, new(registration.Flow).TableName(ctx), "selfservice_registration_flow_id") if err != nil { return nil, err } diff --git a/persistence/sql/persister_verification_code.go b/persistence/sql/persister_verification_code.go index 3c3fc6d9bed5..1186712cdac9 100644 --- a/persistence/sql/persister_verification_code.go +++ b/persistence/sql/persister_verification_code.go @@ -55,7 +55,7 @@ func (p *Persister) UseVerificationCode(ctx context.Context, flowID uuid.UUID, u ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseVerificationCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.VerificationCode, *code.VerificationCode](ctx, p, flowID, userProvidedCode, new(verification.Flow).TableName(ctx), "selfservice_verification_flow_id") + codeRow, err := useOneTimeCode[code.VerificationCode](ctx, p, flowID, userProvidedCode, new(verification.Flow).TableName(ctx), "selfservice_verification_flow_id") if err != nil { return nil, err } diff --git a/selfservice/flow/login/handler.go b/selfservice/flow/login/handler.go index 267b33217216..f98ba66cd3fb 100644 --- a/selfservice/flow/login/handler.go +++ b/selfservice/flow/login/handler.go @@ -233,7 +233,7 @@ preLoginHook: // We only apply the filter on AAL1, because the OIDC strategy can only satsify // AAL1. strategyFilters = []StrategyFilter{func(s Strategy) bool { - return s.ID() == identity.CredentialsTypeOIDC + return s.ID() == identity.CredentialsTypeOIDC || s.ID() == identity.CredentialsTypeSAML }} } } diff --git a/selfservice/flow/registration/handler.go b/selfservice/flow/registration/handler.go index de52abefd5a7..c3f46d3a8397 100644 --- a/selfservice/flow/registration/handler.go +++ b/selfservice/flow/registration/handler.go @@ -141,7 +141,9 @@ func (h *Handler) NewRegistrationFlow(w http.ResponseWriter, r *http.Request, ft h.d.Logger().WithError(err).Warnf("ignoring invalid UUID %q in query parameter `organization`", rawOrg) } else { f.OrganizationID = uuid.NullUUID{UUID: orgID, Valid: true} - strategyFilters = []StrategyFilter{func(s Strategy) bool { return s.ID() == identity.CredentialsTypeOIDC }} + strategyFilters = []StrategyFilter{func(s Strategy) bool { + return s.ID() == identity.CredentialsTypeOIDC || s.ID() == identity.CredentialsTypeSAML + }} } } for _, s := range h.d.RegistrationStrategies(r.Context(), strategyFilters...) { diff --git a/selfservice/flow/registration/handler_test.go b/selfservice/flow/registration/handler_test.go index a9e7b842718c..c2767bdd4192 100644 --- a/selfservice/flow/registration/handler_test.go +++ b/selfservice/flow/registration/handler_test.go @@ -426,7 +426,7 @@ func TestOIDCStrategyOrder(t *testing.T) { // reorder the strategies reg.WithSelfserviceStrategies(t, []any{ - oidc.NewStrategy(reg), + oidc.NewStrategy(reg, oidc.ForCredentialType(identity.CredentialsTypeOIDC)), password.NewStrategy(reg), }) diff --git a/selfservice/hook/web_hook.go b/selfservice/hook/web_hook.go index 6be52d2ca8d0..d756338b13e6 100644 --- a/selfservice/hook/web_hook.go +++ b/selfservice/hook/web_hook.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "net/http" "net/textproto" "time" @@ -21,7 +22,6 @@ import ( "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.11.0" "go.opentelemetry.io/otel/trace" - "golang.org/x/exp/maps" grpccodes "google.golang.org/grpc/codes" "github.com/ory/herodot" diff --git a/selfservice/hook/web_hook_integration_test.go b/selfservice/hook/web_hook_integration_test.go index 0dff20cbb5d0..700835309345 100644 --- a/selfservice/hook/web_hook_integration_test.go +++ b/selfservice/hook/web_hook_integration_test.go @@ -14,6 +14,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "slices" "strconv" "sync" "testing" @@ -27,7 +28,6 @@ import ( "go.opentelemetry.io/otel/attribute" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" - "golang.org/x/exp/slices" "github.com/ory/kratos/driver/config" "github.com/ory/kratos/identity" diff --git a/selfservice/strategy/code/test/persistence.go b/selfservice/strategy/code/test/persistence.go index d7600e9fbd9a..975e63eb6ecc 100644 --- a/selfservice/strategy/code/test/persistence.go +++ b/selfservice/strategy/code/test/persistence.go @@ -5,6 +5,9 @@ package code import ( "context" + "errors" + "sync" + "sync/atomic" "testing" "time" @@ -113,13 +116,27 @@ func TestPersister(ctx context.Context, p interface { _, err := p.CreateRecoveryCode(ctx, dto) require.NoError(t, err) - for i := 1; i <= 5; i++ { - _, err = p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") - require.Error(t, err) + var tooOften, wrongCode int32 + var wg sync.WaitGroup + for range 50 { + wg.Add(1) + go func() { + defer wg.Done() + _, err := p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") + if !assert.Error(t, err) { + return + } + if errors.Is(err, code.ErrCodeSubmittedTooOften) { + atomic.AddInt32(&tooOften, 1) + } else { + atomic.AddInt32(&wrongCode, 1) + } + }() } + wg.Wait() - _, err = p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") - require.ErrorIs(t, err, code.ErrCodeSubmittedTooOften) + require.EqualValues(t, 50, wrongCode+tooOften, "all 50 attempts made") + require.LessOrEqual(t, wrongCode, int32(5), "max. 5 attempts have gone past the duplication check") // Submit again, just to be sure _, err = p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") diff --git a/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json b/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json index 73e408add0a0..4517b39e7474 100644 --- a/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json +++ b/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json @@ -20,7 +20,7 @@ "type": "text", "value": "", "required": true, - "autocomplete": "username email", + "autocomplete": "username webauthn", "disabled": false, "node_type": "input" }, diff --git a/selfservice/strategy/idfirst/strategy_login.go b/selfservice/strategy/idfirst/strategy_login.go index 1f745a2aba07..edf83c487442 100644 --- a/selfservice/strategy/idfirst/strategy_login.go +++ b/selfservice/strategy/idfirst/strategy_login.go @@ -136,7 +136,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, if !ok { continue } - attrs.Autocomplete = "username email" + attrs.Autocomplete = "username webauthn" attrs.Type = node.InputAttributeTypeHidden f.UI.Nodes[k].Attributes = attrs @@ -186,7 +186,7 @@ func (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Requ } f.UI.SetNode(node.NewInputField("identifier", "", s.NodeGroup(), node.InputAttributeTypeText, node.WithInputAttributes(func(a *node.InputAttributes) { - a.Autocomplete = "username email" + a.Autocomplete = "username webauthn" a.Required = true })).WithMetaLabel(identifierLabel)) f.UI.GetNodes().Append(node.NewInputField("method", s.ID(), s.NodeGroup(), node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelContinue())) diff --git a/selfservice/strategy/oidc/provider_config.go b/selfservice/strategy/oidc/provider_config.go index d4967d9eb550..98a5e31726fc 100644 --- a/selfservice/strategy/oidc/provider_config.go +++ b/selfservice/strategy/oidc/provider_config.go @@ -5,11 +5,11 @@ package oidc import ( "encoding/json" + "maps" "net/url" "strings" "github.com/pkg/errors" - "golang.org/x/exp/maps" "github.com/ory/herodot" "github.com/ory/x/urlx" @@ -177,7 +177,8 @@ var supportedProviders = map[string]func(config *Configuration, reg Dependencies "patreon": NewProviderPatreon, "lark": NewProviderLark, "x": NewProviderX, - "line": NewProviderLineV21, + "line": NewProviderLineV21, + "jackson": NewProviderJackson, } func (c ConfigurationCollection) Provider(id string, reg Dependencies) (Provider, error) { diff --git a/selfservice/strategy/oidc/provider_jackson.go b/selfservice/strategy/oidc/provider_jackson.go new file mode 100644 index 000000000000..f83a88306e62 --- /dev/null +++ b/selfservice/strategy/oidc/provider_jackson.go @@ -0,0 +1,57 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package oidc + +import ( + "context" + "strings" + + "github.com/coreos/go-oidc/v3/oidc" + "golang.org/x/oauth2" + + "github.com/ory/x/urlx" +) + +type ProviderJackson struct { + *ProviderGenericOIDC +} + +func NewProviderJackson( + config *Configuration, + reg Dependencies, +) Provider { + return &ProviderJackson{ + ProviderGenericOIDC: &ProviderGenericOIDC{ + config: config, + reg: reg, + }, + } +} + +func (j *ProviderJackson) setProvider(ctx context.Context) { + if j.ProviderGenericOIDC.p == nil { + internalHost := strings.TrimSuffix(j.config.TokenURL, "/api/oauth/token") + config := oidc.ProviderConfig{ + IssuerURL: j.config.IssuerURL, + AuthURL: j.config.AuthURL, + TokenURL: j.config.TokenURL, + DeviceAuthURL: "", + UserInfoURL: internalHost + "/api/oauth/userinfo", + JWKSURL: internalHost + "/oauth/jwks", + Algorithms: []string{"RS256"}, + } + j.ProviderGenericOIDC.p = config.NewProvider(j.withHTTPClientContext(ctx)) + } +} + +func (j *ProviderJackson) OAuth2(ctx context.Context) (*oauth2.Config, error) { + j.setProvider(ctx) + endpoint := j.ProviderGenericOIDC.p.Endpoint() + config := j.oauth2ConfigFromEndpoint(ctx, endpoint) + config.RedirectURL = urlx.AppendPaths( + j.reg.Config().SAMLRedirectURIBase(ctx), + "/self-service/methods/saml/callback/"+j.config.ID).String() + + return config, nil +} diff --git a/selfservice/strategy/oidc/provider_jackson_test.go b/selfservice/strategy/oidc/provider_jackson_test.go new file mode 100644 index 000000000000..4c8bfa0bbf55 --- /dev/null +++ b/selfservice/strategy/oidc/provider_jackson_test.go @@ -0,0 +1,36 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package oidc_test + +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ory/kratos/internal" + "github.com/ory/kratos/selfservice/strategy/oidc" +) + +func TestProviderJackson(t *testing.T) { + _, reg := internal.NewVeryFastRegistryWithoutDB(t) + + j := oidc.NewProviderJackson(&oidc.Configuration{ + Provider: "jackson", + IssuerURL: "https://www.jackson.com/oauth", + AuthURL: "https://www.jackson.com/oauth/auth", + TokenURL: "https://www.jackson.com/api/oauth/token", + Mapper: "file://./stub/hydra.schema.json", + Scope: []string{"email", "profile"}, + ID: "some-id", + }, reg) + assert.NotNil(t, j) + + c, err := j.(oidc.OAuth2Provider).OAuth2(context.Background()) + require.NoError(t, err) + + assert.True(t, strings.HasSuffix(c.RedirectURL, "/self-service/methods/saml/callback/some-id")) +} diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index d799c9190dcd..06af7be1ce58 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "encoding/json" + "maps" "net/http" "net/url" "path/filepath" @@ -20,7 +21,6 @@ import ( "github.com/tidwall/gjson" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "golang.org/x/exp/maps" "golang.org/x/oauth2" "github.com/ory/herodot" @@ -44,7 +44,6 @@ import ( "github.com/ory/kratos/x" "github.com/ory/x/decoderx" "github.com/ory/x/jsonnetsecure" - "github.com/ory/x/jsonx" "github.com/ory/x/otelx" "github.com/ory/x/sqlxx" "github.com/ory/x/stringsx" @@ -119,9 +118,12 @@ func isForced(req interface{}) bool { // Strategy implements selfservice.LoginStrategy, selfservice.RegistrationStrategy and selfservice.SettingsStrategy. // It supports login, registration and settings via OpenID Providers. type Strategy struct { - d Dependencies - validator *schema.Validator - dec *decoderx.HTTP + d Dependencies + validator *schema.Validator + dec *decoderx.HTTP + credType identity.CredentialsType + handleUnknownProviderError func(err error) error + handleMethodNotAllowedError func(err error) error } type AuthCodeContainer struct { @@ -203,15 +205,42 @@ func (s *Strategy) redirectToGET(w http.ResponseWriter, r *http.Request, _ httpr http.Redirect(w, r, dest.String(), http.StatusFound) } -func NewStrategy(d any) *Strategy { - return &Strategy{ - d: d.(Dependencies), - validator: schema.NewValidator(), +type NewStrategyOpt func(s *Strategy) + +// ForCredentialType overrides the credentials type for this strategy. +func ForCredentialType(ct identity.CredentialsType) NewStrategyOpt { + return func(s *Strategy) { s.credType = ct } +} + +// WithUnknownProviderHandler overrides the error returned when the provider +// cannot be found. +func WithUnknownProviderHandler(handler func(error) error) NewStrategyOpt { + return func(s *Strategy) { s.handleUnknownProviderError = handler } +} + +// WithHandleMethodNotAllowedError overrides the error returned when method is +// not allowed. +func WithHandleMethodNotAllowedError(handler func(error) error) NewStrategyOpt { + return func(s *Strategy) { s.handleMethodNotAllowedError = handler } +} + +func NewStrategy(d any, opts ...NewStrategyOpt) *Strategy { + s := &Strategy{ + d: d.(Dependencies), + validator: schema.NewValidator(), + credType: identity.CredentialsTypeOIDC, + handleUnknownProviderError: func(err error) error { return err }, + handleMethodNotAllowedError: func(err error) error { return err }, + } + for _, opt := range opts { + opt(s) } + + return s } func (s *Strategy) ID() identity.CredentialsType { - return identity.CredentialsTypeOIDC + return s.credType } func (s *Strategy) validateFlow(ctx context.Context, r *http.Request, rid uuid.UUID) (flow.Flow, error) { @@ -432,7 +461,7 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt return } - span.SetAttributes(attribute.StringSlice("claims", maps.Keys(claims.RawClaims))) + span.SetAttributes(attribute.StringSlice("claims", slices.Collect(maps.Keys(claims.RawClaims)))) switch a := req.(type) { case *login.Flow: @@ -516,8 +545,8 @@ func (s *Strategy) Config(ctx context.Context) (*ConfigurationCollection, error) var c ConfigurationCollection conf := s.d.Config().SelfServiceStrategy(ctx, string(s.ID())).Config - if err := jsonx. - NewStrictDecoder(bytes.NewBuffer(conf)). + if err := json. + NewDecoder(bytes.NewBuffer(conf)). Decode(&c); err != nil { s.d.Logger().WithError(err).WithField("config", conf) return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to decode OpenID Connect Provider configuration: %s", err)) @@ -530,7 +559,7 @@ func (s *Strategy) provider(ctx context.Context, id string) (Provider, error) { if c, err := s.Config(ctx); err != nil { return nil, err } else if provider, err := c.Provider(id, s.d); err != nil { - return nil, err + return nil, s.handleUnknownProviderError(err) } else { return provider, nil } diff --git a/selfservice/strategy/oidc/strategy_login.go b/selfservice/strategy/oidc/strategy_login.go index 773a500d59a3..392009ec2241 100644 --- a/selfservice/strategy/oidc/strategy_login.go +++ b/selfservice/strategy/oidc/strategy_login.go @@ -102,7 +102,7 @@ func (s *Strategy) processLogin(ctx context.Context, w http.ResponseWriter, r *h ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.processLogin") defer otelx.End(span, &err) - i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeOIDC, identity.OIDCUniqueID(provider.Config().ID, claims.Subject)) + i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), identity.OIDCUniqueID(provider.Config().ID, claims.Subject)) if err != nil { if errors.Is(err, sqlcon.ErrNoRows) { // If no account was found we're "manually" creating a new registration flow and redirecting the browser @@ -218,7 +218,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(ctx, w, r, f, pid, nil, s.handleMethodNotAllowedError(err)) } provider, err := s.provider(ctx, pid) diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index cf7dd35bbba6..5ed061119e7b 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -181,7 +181,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat } if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(ctx, w, r, f, pid, nil, s.handleMethodNotAllowedError(err)) } provider, err := s.provider(ctx, pid) @@ -347,7 +347,7 @@ func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWrite } i.SetCredentials(s.ID(), *creds) - if err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, identity.CredentialsTypeOIDC, provider.Config().ID, provider.Config().OrganizationID, rf, i); err != nil { + if err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, s.ID(), provider.Config().ID, provider.Config().OrganizationID, rf, i); err != nil { return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err) } diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json index 39b1e8a8ca59..ce6f722551f9 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json @@ -38,7 +38,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json index 269754d1dbd0..46a7896863a1 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json index 269754d1dbd0..46a7896863a1 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index 354fdfab6feb..65ade1871cfe 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -110,7 +110,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index 3065bddabb0f..293a8752d52b 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -62,7 +62,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json index 9ea8913db0aa..7465a9a5ae82 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json index 0d33b6d7d9fb..c586635ce49a 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json @@ -18,7 +18,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json index 911497b207da..e65526e82d48 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json index c0d75cd7cd1d..2068eb38ef1c 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json index c0d75cd7cd1d..2068eb38ef1c 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json index 4d8766c503b9..7f1f252857ec 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json @@ -42,7 +42,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json index d26936d42077..d3e3d320af14 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json index d26936d42077..d3e3d320af14 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index 1d38764e30a6..c335744d6532 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -116,7 +116,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index 628b00fd8b5f..ff26034abc11 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -68,7 +68,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json index bd8b5253db96..9f84956e3f6c 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json index bd8b5253db96..9f84956e3f6c 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json index bd8b5253db96..9f84956e3f6c 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json index 4d51e6ea1536..733a28311ebd 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json index 4d51e6ea1536..733a28311ebd 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/spec/api.json b/spec/api.json index 907845a46f7c..6f31f07b7172 100644 --- a/spec/api.json +++ b/spec/api.json @@ -1030,7 +1030,7 @@ "type": "array" }, "type": { - "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\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", + "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", "enum": [ "password", "oidc", @@ -1040,11 +1040,12 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, "updated_at": { "description": "UpdatedAt is a helper struct field for gobuffalo.pop.", @@ -1328,7 +1329,7 @@ "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.", "properties": { "active": { - "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\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", + "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", "enum": [ "password", "oidc", @@ -1338,11 +1339,12 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, "created_at": { "description": "CreatedAt is a helper struct field for gobuffalo.pop.", @@ -1783,7 +1785,7 @@ "registrationFlow": { "properties": { "active": { - "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\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", + "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", "enum": [ "password", "oidc", @@ -1793,11 +1795,12 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, "expires_at": { "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.", @@ -2228,7 +2231,7 @@ "$ref": "#/components/schemas/uiNodeAttributes" }, "group": { - "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", + "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", "enum": [ "default", "password", @@ -2240,10 +2243,11 @@ "lookup_secret", "webauthn", "passkey", - "identifier_first" + "identifier_first", + "captcha" ], "type": "string", - "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" + "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" }, "messages": { "$ref": "#/components/schemas/uiTexts" @@ -3930,7 +3934,7 @@ }, "/admin/identities": { "get": { - "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system.", + "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.", "operationId": "listIdentities", "parameters": [ { @@ -3991,7 +3995,7 @@ "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." }, { - "description": "List of ids used to filter identities.\nIf this list is empty, then no filter will be applied.", + "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).", "in": "query", "name": "ids", "schema": { @@ -4029,7 +4033,7 @@ } }, { - "description": "OrganizationID is the organization id to filter identities by.\n\nIf `ids` is set, this parameter is ignored.", + "description": "List identities that belong to a specific organization.", "in": "query", "name": "organization_id", "schema": { @@ -4271,6 +4275,7 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], @@ -4510,7 +4515,7 @@ } }, { - "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\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", + "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", "in": "path", "name": "type", "required": true, @@ -4524,12 +4529,13 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string" }, - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, { "description": "Identifier is the identifier of the OIDC credential to delete.\nFind the identifier by calling the `GET /admin/identities/{id}?include_credential=oidc` endpoint.", diff --git a/spec/swagger.json b/spec/swagger.json index 031ad06841ba..38c8d2d8555e 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -171,7 +171,7 @@ "oryAccessToken": [] } ], - "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system.", + "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.", "produces": [ "application/json" ], @@ -237,7 +237,7 @@ "items": { "type": "string" }, - "description": "List of ids used to filter identities.\nIf this list is empty, then no filter will be applied.", + "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).", "name": "ids", "in": "query" }, @@ -264,7 +264,7 @@ }, { "type": "string", - "description": "OrganizationID is the organization id to filter identities by.\n\nIf `ids` is set, this parameter is ignored.", + "description": "List identities that belong to a specific organization.", "name": "organization_id", "in": "query" } @@ -441,6 +441,7 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], @@ -702,12 +703,13 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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", - "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\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", + "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", + "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", "name": "type", "in": "path", "required": true @@ -4181,7 +4183,7 @@ } }, "type": { - "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\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", + "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", "type": "string", "enum": [ "password", @@ -4192,10 +4194,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, "updated_at": { "description": "UpdatedAt is a helper struct field for gobuffalo.pop.", @@ -4490,7 +4493,7 @@ ], "properties": { "active": { - "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\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", + "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", "type": "string", "enum": [ "password", @@ -4501,10 +4504,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, "created_at": { "description": "CreatedAt is a helper struct field for gobuffalo.pop.", @@ -4916,7 +4920,7 @@ ], "properties": { "active": { - "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\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", + "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", "type": "string", "enum": [ "password", @@ -4927,10 +4931,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, "expires_at": { "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.", @@ -5078,7 +5083,7 @@ "format": "date-time" }, "method": { - "description": "The method used in this authenticator.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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", + "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", "type": "string", "enum": [ "password", @@ -5089,10 +5094,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\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" + "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" }, "organization": { "description": "The Organization id used for authentication", @@ -5343,7 +5349,7 @@ "$ref": "#/definitions/uiNodeAttributes" }, "group": { - "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", + "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", "type": "string", "enum": [ "default", @@ -5356,9 +5362,10 @@ "lookup_secret", "webauthn", "passkey", - "identifier_first" + "identifier_first", + "captcha" ], - "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" + "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" }, "messages": { "$ref": "#/definitions/uiTexts" diff --git a/test/e2e/package-lock.json b/test/e2e/package-lock.json index 7dd8c6416648..631f255818ff 100644 --- a/test/e2e/package-lock.json +++ b/test/e2e/package-lock.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@ory/kratos-client": "1.2.0", - "@playwright/test": "1.44.1", + "@playwright/test": "1.49.1", "@types/async-retry": "1.4.5", "@types/node": "16.9.6", "@types/yamljs": "0.2.31", @@ -195,19 +195,18 @@ } }, "node_modules/@playwright/test": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", - "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "playwright": "1.44.1" + "playwright": "1.49.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@sideway/address": { @@ -967,9 +966,9 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -1471,7 +1470,6 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -2455,35 +2453,33 @@ } }, "node_modules/playwright": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", - "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.44.1" + "playwright-core": "1.49.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", - "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", "dev": true, - "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/prettier": { @@ -3346,12 +3342,12 @@ } }, "@playwright/test": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", - "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", "dev": true, "requires": { - "playwright": "1.44.1" + "playwright": "1.49.1" } }, "@sideway/address": { @@ -3961,9 +3957,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -5093,19 +5089,19 @@ "dev": true }, "playwright": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", - "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.44.1" + "playwright-core": "1.49.1" } }, "playwright-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", - "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", "dev": true }, "prettier": { diff --git a/test/e2e/package.json b/test/e2e/package.json index a33ac330bf00..001f4d5a4ab3 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@ory/kratos-client": "1.2.0", - "@playwright/test": "1.44.1", + "@playwright/test": "1.49.1", "@types/async-retry": "1.4.5", "@types/node": "16.9.6", "@types/yamljs": "0.2.31", diff --git a/ui/node/node.go b/ui/node/node.go index 66e1b72fa8b6..7d9137db20ff 100644 --- a/ui/node/node.go +++ b/ui/node/node.go @@ -50,6 +50,7 @@ const ( WebAuthnGroup UiNodeGroup = "webauthn" PasskeyGroup UiNodeGroup = "passkey" IdentifierFirstGroup UiNodeGroup = "identifier_first" + CaptchaGroup UiNodeGroup = "captcha" // Available in OEL ) func (g UiNodeGroup) String() string { diff --git a/x/webauthnx/aaguid/aaguid.go b/x/webauthnx/aaguid/aaguid.go index 376303848bc9..b3875fdf1117 100644 --- a/x/webauthnx/aaguid/aaguid.go +++ b/x/webauthnx/aaguid/aaguid.go @@ -8,9 +8,9 @@ package aaguid import ( _ "embed" "encoding/json" + "maps" "github.com/gofrs/uuid" - "golang.org/x/exp/maps" ) var ( diff --git a/x/webauthnx/js/webauthn.js b/x/webauthnx/js/webauthn.js index 4bc0d4427aa9..790896c1926b 100644 --- a/x/webauthnx/js/webauthn.js +++ b/x/webauthnx/js/webauthn.js @@ -143,8 +143,8 @@ const identifierEl = document.getElementsByName("identifier")[0] if (!dataEl || !resultEl || !identifierEl) { - console.debug( - "__oryPasskeyLoginAutocompleteInit: mandatory fields not found", + console.error( + "Unable to initialize WebAuthn / Passkey autocomplete because one or more required form fields are missing.", ) return } @@ -154,9 +154,10 @@ !window.PublicKeyCredential.isConditionalMediationAvailable || window.Cypress // Cypress auto-fills the autocomplete, which we don't want ) { - console.log("This browser does not support WebAuthn!") + console.log("This browser does not support Passkey / WebAuthn!") return } + const isCMA = await PublicKeyCredential.isConditionalMediationAvailable() if (!isCMA) { console.log( @@ -172,6 +173,14 @@ } opt.publicKey.challenge = __oryWebAuthnBufferDecode(opt.publicKey.challenge) + // If this is set we already have a request ongoing which we need to abort. + if (window.abortPasskeyConditionalUI) { + window.abortPasskeyConditionalUI.abort( + "Canceling Passkey autocomplete to complete trigger-based passkey login.", + ) + window.abortPasskeyConditionalUI = undefined + } + // Allow aborting through a global variable window.abortPasskeyConditionalUI = new AbortController() @@ -182,7 +191,6 @@ signal: abortPasskeyConditionalUI.signal, }) .then(function (credential) { - console.trace(credential) resultEl.value = JSON.stringify({ id: credential.id, rawId: __oryWebAuthnBufferEncode(credential.rawId), @@ -214,7 +222,9 @@ const resultEl = document.getElementsByName("passkey_login")[0] if (!dataEl || !resultEl) { - console.debug("__oryPasskeyLogin: mandatory fields not found") + console.error( + "Unable to initialize WebAuthn / Passkey autocomplete because one or more required form fields are missing.", + ) return } if (!window.PublicKeyCredential) { @@ -239,10 +249,12 @@ ) } - window.abortPasskeyConditionalUI && + if (window.abortPasskeyConditionalUI) { window.abortPasskeyConditionalUI.abort( "Canceling Passkey autocomplete to complete trigger-based passkey login.", ) + window.abortPasskeyConditionalUI = undefined + } navigator.credentials .get({ @@ -279,7 +291,9 @@ } console.trace(err) - window.abortPasskeyConditionalUI && __oryPasskeyLoginAutocompleteInit() + + // Try re-initializing autocomplete + return __oryPasskeyLoginAutocompleteInit() }) }