diff --git a/.github/workflows/console.yml b/.github/workflows/console.yml index bc4c5f0898..8471bf5af8 100644 --- a/.github/workflows/console.yml +++ b/.github/workflows/console.yml @@ -30,6 +30,7 @@ jobs: - uses: actions/cache/restore@v3 timeout-minutes: 1 + continue-on-error: true id: cache with: key: console-${{ hashFiles('console', 'proto', '!console/dist') }} diff --git a/.github/workflows/core-integration-test.yml b/.github/workflows/core-integration-test.yml index c99987d25a..f57a79767e 100644 --- a/.github/workflows/core-integration-test.yml +++ b/.github/workflows/core-integration-test.yml @@ -56,6 +56,7 @@ jobs: uses: actions/cache/restore@v3 id: cache timeout-minutes: 1 + continue-on-error: true name: restore previous results with: key: integration-test-postgres-${{ inputs.core_cache_key }} diff --git a/.github/workflows/core-unit-test.yml b/.github/workflows/core-unit-test.yml index c831758d42..3097cfd57d 100644 --- a/.github/workflows/core-unit-test.yml +++ b/.github/workflows/core-unit-test.yml @@ -40,6 +40,7 @@ jobs: uses: actions/cache/restore@v3 id: cache timeout-minutes: 1 + continue-on-error: true name: restore previous results with: key: unit-test-${{ inputs.core_cache_key }} diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index c5d8685334..45c4bbda02 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -41,6 +41,7 @@ jobs: - uses: actions/cache/restore@v3 timeout-minutes: 1 + continue-on-error: true id: cache with: key: core-${{ hashFiles( 'go.*', 'openapi', 'cmd', 'pkg/grpc/**/*.go', 'proto', 'internal') }} diff --git a/Makefile b/Makefile index 6495993ede..dd07fe49c4 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ console_build: console_dependencies console_client .PHONY: clean clean: - $(RM) .artifacts/grpc + $(RM) -r .artifacts/grpc $(RM) $(gen_authopt_path) $(RM) $(gen_zitadel_path) diff --git a/build/workflow.Dockerfile b/build/workflow.Dockerfile index c9fb7e6c2b..db27daf91c 100644 --- a/build/workflow.Dockerfile +++ b/build/workflow.Dockerfile @@ -103,7 +103,7 @@ COPY --from=core-assets /go/src/github.com/zitadel/zitadel/internal ./internal # ####################################### # download console dependencies # ####################################### -FROM node:18-buster AS console-deps +FROM node:20-buster AS console-deps WORKDIR /zitadel/console @@ -115,7 +115,7 @@ RUN yarn install --frozen-lockfile # ####################################### # generate console client # ####################################### -FROM node:18-buster AS console-client +FROM node:20-buster AS console-client WORKDIR /zitadel/console diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 7897e12834..a681bedca2 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -767,7 +767,7 @@ DefaultInstance: PreHeader: Verify email Subject: Verify email Greeting: Hello {{.DisplayName}}, - Text: A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you din't add a new email, please ignore this email. + Text: A new email has been added. Please use the button below to verify your email. (Code {{.Code}}) If you din't add a new email, please ignore this email. ButtonText: Verify email - MessageTextType: VerifyPhone Language: en diff --git a/cmd/setup/steps.yaml b/cmd/setup/steps.yaml index 1497b7be4a..c585c535b2 100644 --- a/cmd/setup/steps.yaml +++ b/cmd/setup/steps.yaml @@ -35,8 +35,8 @@ FirstInstance: # If FirstInstance.Org.Machine.Machine is defined, a service user is created with the IAM_OWNER role, not a human user. Machine: Machine: - Username: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_USERNAME - Name: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_NAME + Username: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME + Name: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME MachineKey: # date format: 2023-01-01T00:00:00Z ExpirationDate: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINEKEY_EXPIRATIONDATE diff --git a/cmd/start/start.go b/cmd/start/start.go index dfc759e3af..744e1f80ac 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -19,7 +19,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/saml/pkg/provider" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts index 6a81838c44..c4a3d64033 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts @@ -157,7 +157,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { this.service .removeMyAuthFactorOTPEmail() .then(() => { - this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); this.cleanupList(); this.getMFAs(); @@ -169,7 +169,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { this.service .removeMyAuthFactorOTPSMS() .then(() => { - this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); this.cleanupList(); this.getMFAs(); diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts index c9be008e01..29418e80fe 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts @@ -102,6 +102,36 @@ export class UserMfaComponent implements OnInit, OnDestroy { .catch((error) => { this.toast.showError(error); }); + } else if (factor.otpEmail) { + this.mgmtUserService + .removeHumanAuthFactorOTPEmail(this.user.id) + .then(() => { + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); + + const index = this.dataSource.data.findIndex((mfa) => !!mfa.otpEmail); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); + }) + .catch((error) => { + this.toast.showError(error); + }); + } else if (factor.otpSms) { + this.mgmtUserService + .removeHumanAuthFactorOTPSMS(this.user.id) + .then(() => { + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); + + const index = this.dataSource.data.findIndex((mfa) => !!mfa.otpSms); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); + }) + .catch((error) => { + this.toast.showError(error); + }); } } }); diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index c80e9e9b49..f2389a25e9 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -322,8 +322,12 @@ import { RemoveCustomLabelPolicyLogoDarkResponse, RemoveCustomLabelPolicyLogoRequest, RemoveCustomLabelPolicyLogoResponse, + RemoveHumanAuthFactorOTPEmailRequest, + RemoveHumanAuthFactorOTPEmailResponse, RemoveHumanAuthFactorOTPRequest, RemoveHumanAuthFactorOTPResponse, + RemoveHumanAuthFactorOTPSMSRequest, + RemoveHumanAuthFactorOTPSMSResponse, RemoveHumanAuthFactorU2FRequest, RemoveHumanAuthFactorU2FResponse, RemoveHumanLinkedIDPRequest, @@ -1805,6 +1809,18 @@ export class ManagementService { return this.grpcService.mgmt.removeHumanAuthFactorU2F(req, null).then((resp) => resp.toObject()); } + public removeHumanAuthFactorOTPSMS(userId: string): Promise { + const req = new RemoveHumanAuthFactorOTPSMSRequest(); + req.setUserId(userId); + return this.grpcService.mgmt.removeHumanAuthFactorOTPSMS(req, null).then((resp) => resp.toObject()); + } + + public removeHumanAuthFactorOTPEmail(userId: string): Promise { + const req = new RemoveHumanAuthFactorOTPEmailRequest(); + req.setUserId(userId); + return this.grpcService.mgmt.removeHumanAuthFactorOTPEmail(req, null).then((resp) => resp.toObject()); + } + public updateHumanProfile( userId: string, firstName?: string, diff --git a/docs/docs/apis/actions/modules.md b/docs/docs/apis/actions/modules.md index 2cc99a222e..6624ff9deb 100644 --- a/docs/docs/apis/actions/modules.md +++ b/docs/docs/apis/actions/modules.md @@ -51,3 +51,58 @@ The object has the following fields and methods: Returns the body as JSON object, or throws an error if the body is not a json object. - `text()` *string* Returns the body + +## UUID + +This module provides functionality to generate a UUID + +### Import + +```js + let uuid = require("zitadel/uuid") +``` + +### `uuid.vX()` function + +This function generates a UUID using [google/uuid](https://github.com/google/uuid). `vX` allows to define the UUID version: + +- `uuid.v1()` *string* + Generates a UUID version 1, based on date-time and MAC address +- `uuid.v3(namespace, data)` *string* + Generates a UUID version 3, based on the provided namespace using MD5 +- `uuid.v4()` *string* + Generates a UUID version 4, which is randomly generated +- `uuid.v5(namespace, data)` *string* + Generates a UUID version 5, based on the provided namespace using SHA1 + +#### Parameters + +- `namespace` *UUID*/*string* + Namespace to be used in the hashing function. Either provide one of defined [namespaces](#namespaces) or a string representing a UUID. +- `data` *[]byte*/*string* + data to be used in the hashing function. Possible types are []byte or string. + +### Namespaces + +The following predefined namespaces can be used for `uuid.v3` and `uuid.v5`: + +- `uuid.namespaceDNS` *UUID* + 6ba7b810-9dad-11d1-80b4-00c04fd430c8 +- `uuid.namespaceURL` *UUID* + 6ba7b811-9dad-11d1-80b4-00c04fd430c8 +- `uuid.namespaceOID` *UUID* + 6ba7b812-9dad-11d1-80b4-00c04fd430c8 +- `uuid.namespaceX500` *UUID* + 6ba7b814-9dad-11d1-80b4-00c04fd430c8 + +### Example +```js +let uuid = require("zitadel/uuid") +function setUUID(ctx, api) { + if (api.metadata === undefined) { + return; + } + + api.v1.user.appendMetadata('custom-id', uuid.v4()); +} +``` \ No newline at end of file diff --git a/docs/docs/legal/onboarding-support.md b/docs/docs/legal/onboarding-support.md new file mode 100644 index 0000000000..e9c76ac38e --- /dev/null +++ b/docs/docs/legal/onboarding-support.md @@ -0,0 +1,87 @@ +--- +title: Description of onboarding support services for ZITADEL +sidebar_label: Onboarding support +custom_edit_url: null +--- + +This annex of the [Framework Agreement](terms-of-service) describes the onboarding support services offered by us for our services. + +Last revised: October 12, 2023 + +Our onboarding support should help you, as a new customer, to get a better understanding on how to integrate ZITADEL into your solution, how to tackle the migration, and ensure a highly-available day-to-day operation. + +Onboarding support services can be offered to customers that enter a ZITADEL Cloud or a ZITADEL Enterprise subscription. + +If you intend to use the open source version exclusively then please join our community chat or Github. +Your questions might help other people in the community and will make our project better over time. + +Please [contact us](https://zitadel.com/contact) for a quote and to get started with onboarding support. +Below you will find topics covered and scope of the offered services. + +## Proof of value + +Within a short time-frame, f.e. 3 weeks, we can show the value of using our services and have the ability to establish the proof a of working setup for your most critical use cases. +We may offer to support you during an initial period to evaluate next steps. +Before the start of the period we may ask you to provide a description of your critical use cases and a high-level overview of your planned integration architecture. +During this period you should make sure that you have the necessary resources on your side to complete the proof of value. + +## Onboarding term + +With the onboarding support we provide the initial knowledge transfer to configure and operate ZITADEL. +During the term you will get direct access to our engineering team via [Technical Account Management](./support-services.md#technical-account-manager). +Duration is typically 3 months but this could vary depending on your requirements. + +We offer an onboarding term in combination with ZITADEL Enterprise subscriptions. + +### Topics covered + +Topics of the onboarding term may include: + +- Administration +- DevOps (Operation) +- Architecture +- Integration +- Migration +- Security Best Practices & Go-Live Checkup + +The scope will be tailored to your requirements. + +More details + +- IAM Configuration +- Walk-though all features +- Users / Manuals +- Authentication & Management APIs +- Validation of tokens +- Client integration best-practices +- Event types +- Database schemas and compute models +- Accessing database +- Observability (Logs, Errors, Metrics, Tracing) +- Operations best practices (Deployment, Backup, Networking etc.) +- Check prerequisites and architecture +- Troubleshoot installation and configuration of ZITADEL +- Troubleshoot and configuration connectivity to the database +- Functional testing of the ZITADEL instance + +
+ Out of scope +
    +
  • Performance testing
  • +
  • Setting up or maintaining backup storage
  • +
  • Running multiple ZITADEL instances on the same cluster
  • +
  • Integration into internal monitoring and alerting
  • +
  • Multi-cluster architecture deployments
  • +
  • DNS, Network and Firewall configuration
  • +
  • Customer-specific Kubernetes configuration needs
  • +
  • Non-production environments
  • +
  • Production deployment
  • +
  • Application-side coding, configuration, or tuning
  • + +
+
+ +## Continuous support + +After the onboarding phase has ended we will provide continuous support according to your subscription. +We can provide you with continued access to the technical account management in our Enterprise subscriptions. diff --git a/docs/docs/legal/support-services.md b/docs/docs/legal/support-services.md index 1c0f36b3fb..1a6f0a1555 100644 --- a/docs/docs/legal/support-services.md +++ b/docs/docs/legal/support-services.md @@ -10,7 +10,7 @@ This annex of the [Framework Agreement](terms-of-service) and the [Support Servi Support Services for products and services provided by ZITADEL is offered to customers according to the terms and conditions outlined in this document. The customer may purchase support services from ZITADEL (CAOS Ltd.) directly. -Last revised: March 15, 2023 +Last revised: October 6, 2023 ## Support Services @@ -82,7 +82,8 @@ Phone Support | +41 43 215 27 34 ZITADEL will enhance its support offering by providing eligible clients with a Technical Account Manager (TAM), who will perform the following tasks for up to the specified amount of time per week during the term of service: - Provide support and advice regarding best practices on platform, product and configuration covered by the applicable Support Services; -- Participate in review calls every other week at mutually agreed times addressing customer’s operational issues. +- Participate in review calls every other week at mutually agreed times addressing customer’s operational challenges or complex support requests; +- Walk-through of new features and customer feedback. We offer TAM services only bundled with specific subscription plans, and the option to add more TAM hours to these plans. If you require consulting for your projects, please request a quote via our [website](https://zitadel.com/contact). diff --git a/docs/docs/support/trainings/application.md b/docs/docs/support/trainings/application.md deleted file mode 100644 index cf313bb961..0000000000 --- a/docs/docs/support/trainings/application.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Application Support Trainings ---- - -## ZITADEL DevOps - -In this session your second level support and operations team will gain an understanding on how to extract relevant information for technical support questions and root cause analysis. We will also present our DevOps best practices and answer your questions. - -**Audience**: 2nd Level Support Staff, Operations -**Duration**: 0.5 day - -**Topics covered**: - -- Event types -- Database schemas and compute models -- Accessing database -- Observability (Logs, Errors, Metrics, Tracing) -- Operations best practices (Deployment, Backup, Networking etc.) -- Q&A - -## ZITADEL Administrator - -In this hands-on training your employees will get a complete overview of the system and learn how to configure and use ZITADEL. Your support staff will gain the required knowledge to provide user-support, while your solution owners gain an understanding about integration of clients. - -**Audience**: 1st / 2nd Level Support Staff, Solution Owner, QA Manager (optional) -**Duration**: 0.5 days - -**Topics covered**: - -- IAM Configuration -- Walk-though all features -- Users / Manuals -- APIs -- Validation of tokens -- Client integration best-practices -- Q&A diff --git a/docs/docs/support/trainings/introduction.md b/docs/docs/support/trainings/introduction.md deleted file mode 100644 index c597470f38..0000000000 --- a/docs/docs/support/trainings/introduction.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: ZITADEL Trainings -sidebar_label: Introduction ---- - -The following pages describe the the trainings provided by ZITADEL. These trainings are intended for onboarding and during the course of a Support Program. - -Training should be held as block-sessions with the relevant staff from your organization. - -## Onboarding Project - -You receive professional onboarding support from our engineers, who help you to setup and configure ZITADEL on your infrastructure. - -[More information](project) - -## Application Support Trainings - -With the application support trainings we provide the initial knowledge transfer to manage and support ZITADEL. The trainings are held as block-sessions with relevant staff from your organization. Prices are flat-fee, excl. expenses. - -* [ZITADEL DevOps](application#zitadel-devops) -* [ZITADEL Administrator](application#zitadel-administrator) - -## Recurring Trainings - -While you can benefit from a technical account manager during your term, these trainings are designed to onboard new staff or update staff about larger changes to the platform. Prices are flat-fee, excl. expenses. - -* [ZITADEL Support Refresher](recurring#zitadel-support-refresher) -* [ZITADEL Support Onboarding](recurring#zitadel-support-onboarding) - -In case you have any questions please [get in touch](https://zitadel.com/contact). diff --git a/docs/docs/support/trainings/project.md b/docs/docs/support/trainings/project.md deleted file mode 100644 index 35f3259b46..0000000000 --- a/docs/docs/support/trainings/project.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Onboarding Project ---- - -Effort required during depends on the complexity of your infrastructure and the overall setup. With a Multi-Zone Setup (excl. Multi-Region), support during this phase requires around 10-25h over 2 weeks. Actual effort is based on time and material. - -Scope of the project is agreed on individual basis. - -## In Scope - -- Check prerequisites and architecture -- Troubleshoot installation and configuration of ZITADEL -- Troubleshoot and configuration connectivity to the database -- Functional testing of the ZITADEL instance - -## Out of Scope - -- Running multiple ZITADEL instances on the same cluster -- Integration into internal monitoring and alerting -- Multi-cluster architecture deployments -- DNS, Network and Firewall configuration -- Customer-specific Kubernetes configuration needs -- Changes for specific environments -- Performance testing -- Production deployment -- Application-side coding, configuration, or tuning -- Changes or configuration on assets used in ZITADEL -- Setting up or maintaining backup storage - -## Prerequisites - -- Running Kubernetes with possibility to deploy to namespaces -- Inbound and outbound HTTP/2 traffic possible -- For being able to send SMS, we need a Twilio sender name, SID and an auth token -- ZITADEL also needs to connect to an email relay of your choice. We need the SMTP host, user and app key as well as the ZITADEL emails sender address and name. diff --git a/docs/docs/support/trainings/recurring.md b/docs/docs/support/trainings/recurring.md deleted file mode 100644 index 29982cf620..0000000000 --- a/docs/docs/support/trainings/recurring.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Recurring Trainings ---- - -## ZITADEL Support Refresher - -In this session you can refresh knowledge about existing and gain experience with new features of ZITADEL to keep the quality of your support high. We recommend an half day training per support staff. - -**Audience**: 1st / 2nd Level Support Staff, Solution Owner, QA Manager (optional) -**Duration**: 0.5 day / support staff - -**Topics covered**: - -* Walk-through new features -* Review of difficult support issues -* Review of customer feedback -* Q&A - -## ZITADEL Support Onboarding - -In this hands-on training new support staff will get an overview of the system and learn how to configure and use ZITADEL to provide support for users. - -**Audience**: 1st / 2nd Level Support Staff, Solution Owner, QA Manager (optional) -**Duration**: 0.5 days / support staff - -**Topics covered**: - -* Event types -* Accessing database -* Logs and Errors -* Validation of tokens -* Walk-through key features -* Q&A diff --git a/docs/sidebars.js b/docs/sidebars.js index 1c82d82852..a2ba179c80 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -425,6 +425,11 @@ module.exports = { items: [ "support/software-release-cycles-support", "support/troubleshooting", + { + type: 'link', + label: 'Support Service Descriptions', + href: '/legal/support-services', + }, { type: 'category', label: "Technical Advisory", @@ -440,17 +445,6 @@ module.exports = { }, ], }, - { - type: "category", - label: "Trainings", - collapsed: true, - items: [ - "support/trainings/introduction", - "support/trainings/application", - "support/trainings/recurring", - "support/trainings/project", - ], - }, ] }, ], @@ -697,6 +691,7 @@ module.exports = { "legal/cloud-service-description", "legal/service-level-description", "legal/support-services", + "legal/onboarding-support", ], }, { diff --git a/go.mod b/go.mod index f4e513fbb1..875f6bb0e8 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/drone/envsubst v1.0.3 github.com/envoyproxy/protoc-gen-validate v1.0.2 github.com/fatih/color v1.15.0 + github.com/go-jose/go-jose/v3 v3.0.0 github.com/go-ldap/ldap/v3 v3.4.5 github.com/go-webauthn/webauthn v0.8.6 github.com/golang/mock v1.6.0 @@ -48,11 +49,12 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/gamut v0.3.1 github.com/muhlemmer/gu v0.3.1 + github.com/muhlemmer/httpforwarded v0.1.0 github.com/nicksnyder/go-i18n/v2 v2.2.1 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 github.com/rakyll/statik v0.1.7 - github.com/rs/cors v1.10.0 + github.com/rs/cors v1.10.1 github.com/sony/sonyflake v1.2.0 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.16.0 @@ -60,36 +62,35 @@ require ( github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 github.com/ttacon/libphonenumber v1.2.1 github.com/zitadel/logging v0.4.0 - github.com/zitadel/oidc/v2 v2.11.0 + github.com/zitadel/oidc/v3 v3.0.2 github.com/zitadel/passwap v0.4.0 github.com/zitadel/saml v0.1.2 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0 - go.opentelemetry.io/otel v1.17.0 + go.opentelemetry.io/otel v1.19.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0 go.opentelemetry.io/otel/exporters/prometheus v0.40.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 - go.opentelemetry.io/otel/metric v1.17.0 + go.opentelemetry.io/otel/metric v1.19.0 go.opentelemetry.io/otel/sdk v1.17.0 go.opentelemetry.io/otel/sdk/metric v0.40.0 - go.opentelemetry.io/otel/trace v1.17.0 - golang.org/x/crypto v0.13.0 - golang.org/x/net v0.15.0 - golang.org/x/oauth2 v0.12.0 + go.opentelemetry.io/otel/trace v1.19.0 + golang.org/x/crypto v0.14.0 + golang.org/x/net v0.17.0 + golang.org/x/oauth2 v0.13.0 golang.org/x/sync v0.3.0 golang.org/x/text v0.13.0 google.golang.org/api v0.138.0 google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 - gopkg.in/square/go-jose.v2 v2.6.0 sigs.k8s.io/yaml v1.3.0 ) require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.43.1 // indirect github.com/crewjam/httperr v0.2.0 // indirect - github.com/dmarkham/enumer v1.5.8 // indirect + github.com/go-chi/chi/v5 v5.0.10 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect @@ -105,14 +106,11 @@ require ( github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/muhlemmer/httpforwarded v0.1.0 // indirect - github.com/pascaldekloe/name v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/smartystreets/assertions v1.0.0 // indirect github.com/zenazn/goji v1.0.1 // indirect + github.com/zitadel/schema v1.3.0 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect ) @@ -154,7 +152,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.3.1 github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect @@ -203,7 +201,7 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/sys v0.12.0 + golang.org/x/sys v0.13.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 9e7ae585c7..018d31f9ef 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,6 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dmarkham/enumer v1.5.8 h1:fIF11F9l5jyD++YYvxcSH5WgHfeaSGPaN/T4kOQ4qEM= -github.com/dmarkham/enumer v1.5.8/go.mod h1:d10o8R3t/gROm2p3BXqTkMt2+HMuxEmWCXzorAruYak= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20230531210528-d7324b2d74f7/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e h1:UvQD6hTSfeM6hhTQ24Dlw2RppP05W7SWbWb6kubJAog= @@ -267,6 +265,8 @@ github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= +github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= @@ -275,6 +275,8 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -720,8 +722,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/name v1.0.0 h1:n7LKFgHixETzxpRv2R77YgPUFo85QHGZKrdaYm7eY5U= -github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= @@ -782,8 +782,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8= -github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -883,12 +883,14 @@ github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8= github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zitadel/logging v0.4.0 h1:lRAIFgaRoJpLNbsL7jtIYHcMDoEJP9QZB4GqMfl4xaA= github.com/zitadel/logging v0.4.0/go.mod h1:6uALRJawpkkuUPCkgzfgcPR3c2N908wqnOnIrRelUFc= -github.com/zitadel/oidc/v2 v2.11.0 h1:Am4/yQr4iiM5bznRgF3FOp+wLdKx2gzSU73uyI9vvBE= -github.com/zitadel/oidc/v2 v2.11.0/go.mod h1:enFSVBQI6aE0TEB1ntjXs9r6O6DEosxX4uhEBLBVD8o= +github.com/zitadel/oidc/v3 v3.0.2 h1:fw0EAjx8lIlDMJ54hDz2fWIhpW/Y13tW5gd1qWGqbr4= +github.com/zitadel/oidc/v3 v3.0.2/go.mod h1:ne9V9FHug4iUZDV42JirWVLHcbmwaxY8LnkcfekHgRg= github.com/zitadel/passwap v0.4.0 h1:cMaISx+Ve7ilgG7Q8xOli4Z6IWr8Gndss+jeBk5A3O0= github.com/zitadel/passwap v0.4.0/go.mod h1:yHaDM4A68yRkdic5BZ4iUNoc19hT+kYt8n1/Nz+I87g= github.com/zitadel/saml v0.1.2 h1:RICwNTuP2upX4A1sZ8iq1rv4/x3DhZHzFx1e5bTKoTo= github.com/zitadel/saml v0.1.2/go.mod h1:M+X+3vMUulpoLofKeH/W1/qjQQ3owitc2GuGDu3oYpM= +github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= +github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -905,8 +907,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0 h1:HKORGpiOY0R0nAPtKx/ub8/7XoHhRooP8yNRkuPfelI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0/go.mod h1:e+y1M74SYXo/FcIx3UATwth2+5dDkM8dBi7eXg1tbw8= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 h1:U5GYackKpVKlPrd/5gKMlrTlP2dCESAAFU682VCpieY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0/go.mod h1:aFsJfCEnLzEu9vRRAcUiB/cpRTbVsNdF3OHSPpdjxZQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0 h1:iGeIsSYwpYSvh5UGzWrJfTDJvPjrXtxl3GUppj6IXQU= @@ -915,14 +917,14 @@ go.opentelemetry.io/otel/exporters/prometheus v0.40.0 h1:9h6lCssr1j5aYVvWT6oc+ER go.opentelemetry.io/otel/exporters/prometheus v0.40.0/go.mod h1:5USWZ0ovyQB5CIM3IO3bGRSoDPMXiT3t+15gu8Zo9HQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= go.opentelemetry.io/otel/sdk/metric v0.40.0 h1:qOM29YaGcxipWjL5FzpyZDpCYrDREvX0mVlmXdOjCHU= go.opentelemetry.io/otel/sdk/metric v0.40.0/go.mod h1:dWxHtdzdJvg+ciJUKLTKwrMe5P6Dv3FyDbh8UkfgkVs= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= @@ -952,6 +954,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -965,8 +968,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 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= @@ -1007,8 +1010,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1059,8 +1060,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 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= @@ -1070,8 +1071,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1154,8 +1155,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1242,8 +1243,6 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1384,8 +1383,6 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:a gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/internal/actions/uuid_module.go b/internal/actions/uuid_module.go new file mode 100644 index 0000000000..15d2992127 --- /dev/null +++ b/internal/actions/uuid_module.go @@ -0,0 +1,83 @@ +package actions + +import ( + "context" + + "github.com/dop251/goja" + "github.com/google/uuid" + "github.com/zitadel/logging" +) + +func WithUUID(ctx context.Context) Option { + return func(c *runConfig) { + c.modules["zitadel/uuid"] = func(runtime *goja.Runtime, module *goja.Object) { + requireUUID(ctx, runtime, module) + } + } +} + +func requireUUID(_ context.Context, runtime *goja.Runtime, module *goja.Object) { + o := module.Get("exports").(*goja.Object) + logging.OnError(o.Set("v1", inRuntime(uuid.NewUUID, runtime))).Warn("unable to set module") + logging.OnError(o.Set("v3", inRuntimeHash(uuid.NewMD5, runtime))).Warn("unable to set module") + logging.OnError(o.Set("v4", inRuntime(uuid.NewRandom, runtime))).Warn("unable to set module") + logging.OnError(o.Set("v5", inRuntimeHash(uuid.NewSHA1, runtime))).Warn("unable to set module") + logging.OnError(o.Set("namespaceDNS", uuid.NameSpaceDNS)).Warn("unable to set namespace") + logging.OnError(o.Set("namespaceURL", uuid.NameSpaceURL)).Warn("unable to set namespace") + logging.OnError(o.Set("namespaceOID", uuid.NameSpaceOID)).Warn("unable to set namespace") + logging.OnError(o.Set("namespaceX500", uuid.NameSpaceX500)).Warn("unable to set namespace") +} + +func inRuntime(function func() (uuid.UUID, error), runtime *goja.Runtime) func(call goja.FunctionCall) goja.Value { + return func(call goja.FunctionCall) goja.Value { + if len(call.Arguments) != 0 { + panic("invalid arg count") + } + + uuid, err := function() + if err != nil { + logging.WithError(err) + panic(err) + } + + return runtime.ToValue(uuid.String()) + } +} + +func inRuntimeHash(function func(uuid.UUID, []byte) uuid.UUID, runtime *goja.Runtime) func(call goja.FunctionCall) goja.Value { + return func(call goja.FunctionCall) goja.Value { + if len(call.Arguments) != 2 { + logging.WithFields("count", len(call.Arguments)).Debug("other than 2 args provided") + panic("invalid arg count") + } + + var err error + var namespace uuid.UUID + switch n := call.Arguments[0].Export().(type) { + case string: + namespace, err = uuid.Parse(n) + if err != nil { + logging.WithError(err).Debug("namespace failed parsing as UUID") + panic(err) + } + case uuid.UUID: + namespace = n + default: + logging.WithError(err).Debug("invalid type for namespace") + panic(err) + } + + var data []byte + switch d := call.Arguments[1].Export().(type) { + case string: + data = []byte(d) + case []byte: + data = d + default: + logging.WithError(err).Debug("invalid type for data") + panic(err) + } + + return runtime.ToValue(function(namespace, data).String()) + } +} diff --git a/internal/api/authz/token.go b/internal/api/authz/token.go index afb4af4bf3..e5b34af9a2 100644 --- a/internal/api/authz/token.go +++ b/internal/api/authz/token.go @@ -10,8 +10,8 @@ import ( "sync" "time" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/go-jose/go-jose/v3" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/crypto" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -28,7 +28,7 @@ type TokenVerifier struct { authZRepo authZRepo clients sync.Map authMethods MethodMapping - systemJWTProfile op.JWTProfileVerifier + systemJWTProfile *op.JWTProfileVerifier } type MembershipsResolver interface { diff --git a/internal/api/grpc/management/information.go b/internal/api/grpc/management/information.go index 29ce6ac01c..d18e115bfa 100644 --- a/internal/api/grpc/management/information.go +++ b/internal/api/grpc/management/information.go @@ -3,7 +3,7 @@ package management import ( "context" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/http" diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index d7a9c56dca..c61832169a 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -4,7 +4,7 @@ import ( "context" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "google.golang.org/protobuf/types/known/durationpb" @@ -647,6 +647,26 @@ func (s *Server) RemoveHumanAuthFactorU2F(ctx context.Context, req *mgmt_pb.Remo }, nil } +func (s *Server) RemoveHumanAuthFactorOTPSMS(ctx context.Context, req *mgmt_pb.RemoveHumanAuthFactorOTPSMSRequest) (*mgmt_pb.RemoveHumanAuthFactorOTPSMSResponse, error) { + objectDetails, err := s.command.RemoveHumanOTPSMS(ctx, req.UserId, authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err + } + return &mgmt_pb.RemoveHumanAuthFactorOTPSMSResponse{ + Details: obj_grpc.DomainToChangeDetailsPb(objectDetails), + }, nil +} + +func (s *Server) RemoveHumanAuthFactorOTPEmail(ctx context.Context, req *mgmt_pb.RemoveHumanAuthFactorOTPEmailRequest) (*mgmt_pb.RemoveHumanAuthFactorOTPEmailResponse, error) { + objectDetails, err := s.command.RemoveHumanOTPEmail(ctx, req.UserId, authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err + } + return &mgmt_pb.RemoveHumanAuthFactorOTPEmailResponse{ + Details: obj_grpc.DomainToChangeDetailsPb(objectDetails), + }, nil +} + func (s *Server) ListHumanPasswordless(ctx context.Context, req *mgmt_pb.ListHumanPasswordlessRequest) (*mgmt_pb.ListHumanPasswordlessResponse, error) { query := new(query.UserAuthMethodSearchQueries) err := query.AppendUserIDQuery(req.UserId) diff --git a/internal/api/grpc/oidc/v2/oidc.go b/internal/api/grpc/oidc/v2/oidc.go index d35a5dcff0..465303b98a 100644 --- a/internal/api/grpc/oidc/v2/oidc.go +++ b/internal/api/grpc/oidc/v2/oidc.go @@ -4,7 +4,7 @@ import ( "context" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" diff --git a/internal/api/grpc/oidc/v2/oidc_integration_test.go b/internal/api/grpc/oidc/v2/oidc_integration_test.go index b64b71325d..db11f9022b 100644 --- a/internal/api/grpc/oidc/v2/oidc_integration_test.go +++ b/internal/api/grpc/oidc/v2/oidc_integration_test.go @@ -54,7 +54,7 @@ func TestServer_GetAuthRequest(t *testing.T) { require.NoError(t, err) client, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) require.NoError(t, err) - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) now := time.Now() @@ -134,7 +134,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "session not found", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -151,7 +151,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "session token invalid", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -168,7 +168,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "fail callback", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -192,7 +192,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "code callback", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -217,7 +217,7 @@ func TestServer_CreateCallback(t *testing.T) { AuthRequestId: func() string { client, err := Tester.CreateOIDCImplicitFlowClient(CTX, redirectURIImplicit) require.NoError(t, err) - authRequestID, err := Tester.CreateOIDCAuthRequestImplicit(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURIImplicit) + authRequestID, err := Tester.CreateOIDCAuthRequestImplicit(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURIImplicit) require.NoError(t, err) return authRequestID }(), diff --git a/internal/api/grpc/oidc/v2/server.go b/internal/api/grpc/oidc/v2/server.go index a7587dfdcf..823594fbd0 100644 --- a/internal/api/grpc/oidc/v2/server.go +++ b/internal/api/grpc/oidc/v2/server.go @@ -1,7 +1,7 @@ package oidc import ( - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "google.golang.org/grpc" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/api/grpc/server/middleware/auth_interceptor.go b/internal/api/grpc/server/middleware/auth_interceptor.go index 96426e8577..d2a81203ea 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor.go +++ b/internal/api/grpc/server/middleware/auth_interceptor.go @@ -33,12 +33,7 @@ func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, return nil, status.Error(codes.Unauthenticated, "auth header missing") } - var orgDomain string - orgID := grpc_util.GetHeader(authCtx, http.ZitadelOrgID) - if o, ok := req.(OrganisationFromRequest); ok { - orgID = o.OrganisationFromRequest().ID - orgDomain = o.OrganisationFromRequest().Domain - } + orgID, orgDomain := orgIDAndDomainFromRequest(authCtx, req) ctxSetter, err := authz.CheckUserAuthorization(authCtx, req, authToken, orgID, orgDomain, verifier, authConfig, authOpt, info.FullMethod) if err != nil { return nil, err @@ -47,11 +42,38 @@ func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, return handler(ctxSetter(ctx), req) } -type OrganisationFromRequest interface { - OrganisationFromRequest() *Organisation +func orgIDAndDomainFromRequest(ctx context.Context, req interface{}) (id, domain string) { + orgID := grpc_util.GetHeader(ctx, http.ZitadelOrgID) + o, ok := req.(OrganizationFromRequest) + if !ok { + return orgID, "" + } + id = o.OrganizationFromRequest().ID + domain = o.OrganizationFromRequest().Domain + if id != "" || domain != "" { + return id, domain + } + // check if the deprecated organisation is used. + // to be removed before going GA (https://github.com/zitadel/zitadel/issues/6718) + id = o.OrganisationFromRequest().ID + domain = o.OrganisationFromRequest().Domain + if id != "" || domain != "" { + return id, domain + } + return orgID, domain } -type Organisation struct { +// Deprecated: will be removed in favor of OrganizationFromRequest (https://github.com/zitadel/zitadel/issues/6718) +type OrganisationFromRequest interface { + OrganisationFromRequest() *Organization +} + +type Organization struct { ID string Domain string } + +type OrganizationFromRequest interface { + OrganizationFromRequest() *Organization + OrganisationFromRequest +} diff --git a/internal/api/grpc/session/v2/session.go b/internal/api/grpc/session/v2/session.go index fe8e0d8744..f98983936d 100644 --- a/internal/api/grpc/session/v2/session.go +++ b/internal/api/grpc/session/v2/session.go @@ -2,10 +2,13 @@ package session import ( "context" + "net" + "net/http" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/muhlemmer/gu" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/grpc/object/v2" "github.com/zitadel/zitadel/internal/command" @@ -41,7 +44,7 @@ func (s *Server) ListSessions(ctx context.Context, req *session.ListSessionsRequ } func (s *Server) CreateSession(ctx context.Context, req *session.CreateSessionRequest) (*session.CreateSessionResponse, error) { - checks, metadata, err := s.createSessionRequestToCommand(ctx, req) + checks, metadata, userAgent, err := s.createSessionRequestToCommand(ctx, req) if err != nil { return nil, err } @@ -50,7 +53,7 @@ func (s *Server) CreateSession(ctx context.Context, req *session.CreateSessionRe return nil, err } - set, err := s.command.CreateSession(ctx, cmds, metadata) + set, err := s.command.CreateSession(ctx, cmds, metadata, userAgent) if err != nil { return nil, err } @@ -113,9 +116,34 @@ func sessionToPb(s *query.Session) *session.Session { Sequence: s.Sequence, Factors: factorsToPb(s), Metadata: s.Metadata, + UserAgent: userAgentToPb(s.UserAgent), } } +func userAgentToPb(ua domain.UserAgent) *session.UserAgent { + if ua.IsEmpty() { + return nil + } + + out := &session.UserAgent{ + FingerprintId: ua.FingerprintID, + Description: ua.Description, + } + if ua.IP != nil { + out.Ip = gu.Ptr(ua.IP.String()) + } + if ua.Header == nil { + return out + } + out.Header = make(map[string]*session.UserAgent_HeaderValues, len(ua.Header)) + for k, v := range ua.Header { + out.Header[k] = &session.UserAgent_HeaderValues{ + Values: v, + } + } + return out +} + func factorsToPb(s *query.Session) *session.Factors { user := userFactorToPb(s.UserFactor) if user == nil { @@ -188,6 +216,7 @@ func userFactorToPb(factor query.SessionUserFactor) *session.UserFactor { LoginName: factor.LoginName, DisplayName: factor.DisplayName, OrganisationId: factor.ResourceOwner, + OrganizationId: factor.ResourceOwner, } } @@ -236,12 +265,30 @@ func idsQueryToQuery(q *session.IDsQuery) (query.SearchQuery, error) { return query.NewSessionIDsSearchQuery(q.Ids) } -func (s *Server) createSessionRequestToCommand(ctx context.Context, req *session.CreateSessionRequest) ([]command.SessionCommand, map[string][]byte, error) { +func (s *Server) createSessionRequestToCommand(ctx context.Context, req *session.CreateSessionRequest) ([]command.SessionCommand, map[string][]byte, *domain.UserAgent, error) { checks, err := s.checksToCommand(ctx, req.Checks) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - return checks, req.GetMetadata(), nil + return checks, req.GetMetadata(), userAgentToCommand(req.GetUserAgent()), nil +} + +func userAgentToCommand(userAgent *session.UserAgent) *domain.UserAgent { + if userAgent == nil { + return nil + } + out := &domain.UserAgent{ + FingerprintID: userAgent.FingerprintId, + IP: net.ParseIP(userAgent.GetIp()), + Description: userAgent.Description, + } + if len(userAgent.Header) > 0 { + out.Header = make(http.Header, len(userAgent.Header)) + for k, values := range userAgent.Header { + out.Header[k] = values.GetValues() + } + } + return out } func (s *Server) setSessionRequestToCommand(ctx context.Context, req *session.SetSessionRequest) ([]command.SessionCommand, error) { diff --git a/internal/api/grpc/session/v2/session_integration_test.go b/internal/api/grpc/session/v2/session_integration_test.go index 1dcad7bc53..9aba59ee37 100644 --- a/internal/api/grpc/session/v2/session_integration_test.go +++ b/internal/api/grpc/session/v2/session_integration_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/proto" "github.com/zitadel/zitadel/internal/integration" object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta" @@ -53,7 +54,7 @@ func TestMain(m *testing.M) { }()) } -func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, window time.Duration, metadata map[string][]byte, factors ...wantFactor) *session.Session { +func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, window time.Duration, metadata map[string][]byte, userAgent *session.UserAgent, factors ...wantFactor) *session.Session { t.Helper() require.NotEmpty(t, id) require.NotEmpty(t, token) @@ -70,6 +71,11 @@ func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, windo assert.WithinRange(t, s.GetChangeDate().AsTime(), time.Now().Add(-window), time.Now().Add(window)) assert.Equal(t, sequence, s.GetSequence()) assert.Equal(t, metadata, s.GetMetadata()) + + if !proto.Equal(userAgent, s.GetUserAgent()) { + t.Errorf("user agent =\n%v\nwant\n%v", s.GetUserAgent(), userAgent) + } + verifyFactors(t, s.GetFactors(), window, factors) return s } @@ -131,11 +137,12 @@ func verifyFactors(t testing.TB, factors *session.Factors, window time.Duration, func TestServer_CreateSession(t *testing.T) { tests := []struct { - name string - req *session.CreateSessionRequest - want *session.CreateSessionResponse - wantErr bool - wantFactors []wantFactor + name string + req *session.CreateSessionRequest + want *session.CreateSessionResponse + wantErr bool + wantFactors []wantFactor + wantUserAgent *session.UserAgent }{ { name: "empty session", @@ -148,6 +155,33 @@ func TestServer_CreateSession(t *testing.T) { }, }, }, + { + name: "user agent", + req: &session.CreateSessionRequest{ + Metadata: map[string][]byte{"foo": []byte("bar")}, + UserAgent: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Ip: gu.Ptr("1.2.3.4"), + Description: gu.Ptr("Description"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + }, + }, + }, + want: &session.CreateSessionResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + wantUserAgent: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Ip: gu.Ptr("1.2.3.4"), + Description: gu.Ptr("Description"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + }, + }, + }, { name: "with user", req: &session.CreateSessionRequest{ @@ -219,7 +253,7 @@ func TestServer_CreateSession(t *testing.T) { require.NoError(t, err) integration.AssertDetails(t, tt.want, got) - verifyCurrentSession(t, got.GetSessionId(), got.GetSessionToken(), got.GetDetails().GetSequence(), time.Minute, tt.req.GetMetadata(), tt.wantFactors...) + verifyCurrentSession(t, got.GetSessionId(), got.GetSessionToken(), got.GetDetails().GetSequence(), time.Minute, tt.req.GetMetadata(), tt.wantUserAgent, tt.wantFactors...) }) } } @@ -242,7 +276,7 @@ func TestServer_CreateSession_webauthn(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) assertionData, err := Tester.WebAuthN.CreateAssertionResponse(createResp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true) require.NoError(t, err) @@ -258,7 +292,7 @@ func TestServer_CreateSession_webauthn(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactorUserVerified) + verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactorUserVerified) } func TestServer_CreateSession_successfulIntent(t *testing.T) { @@ -274,7 +308,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, User.GetUserId(), "id") updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{ @@ -288,7 +322,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantIntentFactor) + verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantIntentFactor) } func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { @@ -304,7 +338,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) idpUserID := "id" intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, "", idpUserID) @@ -331,7 +365,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantIntentFactor) + verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantIntentFactor) } func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { @@ -347,7 +381,7 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) intentID := Tester.CreateIntent(t, idpID) _, err = Client.SetSession(CTX, &session.SetSessionRequest{ @@ -399,7 +433,7 @@ func TestServer_SetSession_flow(t *testing.T) { createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{}) require.NoError(t, err) sessionToken := createResp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, createResp.GetDetails().GetSequence(), time.Minute, nil, nil) t.Run("check user", func(t *testing.T) { resp, err := Client.SetSession(CTX, &session.SetSessionRequest{ @@ -415,7 +449,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor) }) t.Run("check webauthn, user verified (passkey)", func(t *testing.T) { @@ -430,7 +464,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true) @@ -447,7 +481,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactorUserVerified) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactorUserVerified) }) userAuthCtx := Tester.WithAuthorizationToken(CTX, sessionToken) @@ -474,7 +508,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), false) @@ -491,7 +525,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor) }) } }) @@ -510,7 +544,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor, wantTOTPFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantTOTPFactor) }) t.Run("check OTP SMS", func(t *testing.T) { @@ -522,7 +556,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() otp := resp.GetChallenges().GetOtpSms() @@ -539,7 +573,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor, wantOTPSMSFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantOTPSMSFactor) }) t.Run("check OTP Email", func(t *testing.T) { @@ -553,7 +587,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() otp := resp.GetChallenges().GetOtpEmail() @@ -570,7 +604,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor, wantOTPEmailFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantOTPEmailFactor) }) } diff --git a/internal/api/grpc/session/v2/session_test.go b/internal/api/grpc/session/v2/session_test.go index 33804caba5..8422b675b5 100644 --- a/internal/api/grpc/session/v2/session_test.go +++ b/internal/api/grpc/session/v2/session_test.go @@ -2,15 +2,19 @@ package session import ( "context" + "net" + "net/http" "testing" "time" + "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/query" @@ -23,7 +27,7 @@ func Test_sessionsToPb(t *testing.T) { past := now.Add(-time.Hour) sessions := []*query.Session{ - { // no factor + { // no factor, with user agent ID: "999", CreationDate: now, ChangeDate: now, @@ -32,6 +36,12 @@ func Test_sessionsToPb(t *testing.T) { ResourceOwner: "me", Creator: "he", Metadata: map[string][]byte{"hello": []byte("world")}, + UserAgent: domain.UserAgent{ + FingerprintID: gu.Ptr("fingerprintID"), + Description: gu.Ptr("description"), + IP: net.IPv4(1, 2, 3, 4), + Header: http.Header{"foo": []string{"foo", "bar"}}, + }, }, { // user factor ID: "999", @@ -114,13 +124,21 @@ func Test_sessionsToPb(t *testing.T) { } want := []*session.Session{ - { // no factor + { // no factor, with user agent Id: "999", CreationDate: timestamppb.New(now), ChangeDate: timestamppb.New(now), Sequence: 123, Factors: nil, Metadata: map[string][]byte{"hello": []byte("world")}, + UserAgent: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerprintID"), + Description: gu.Ptr("description"), + Ip: gu.Ptr("1.2.3.4"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + }, + }, }, { // user factor Id: "999", @@ -134,6 +152,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, }, Metadata: map[string][]byte{"hello": []byte("world")}, @@ -150,6 +169,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, Password: &session.PasswordFactor{ VerifiedAt: timestamppb.New(past), @@ -169,6 +189,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, WebAuthN: &session.WebAuthNFactor{ VerifiedAt: timestamppb.New(past), @@ -189,6 +210,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, Totp: &session.TOTPFactor{ VerifiedAt: timestamppb.New(past), @@ -208,6 +230,71 @@ func Test_sessionsToPb(t *testing.T) { } } +func Test_userAgentToPb(t *testing.T) { + type args struct { + ua domain.UserAgent + } + tests := []struct { + name string + args args + want *session.UserAgent + }{ + { + name: "empty", + args: args{domain.UserAgent{}}, + }, + { + name: "fingerprint id and description", + args: args{domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + }}, + want: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + }, + }, + { + name: "with ip", + args: args{domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + IP: net.IPv4(1, 2, 3, 4), + }}, + want: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + Ip: gu.Ptr("1.2.3.4"), + }, + }, + { + name: "with header", + args: args{domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + Header: http.Header{ + "foo": []string{"foo", "bar"}, + "hello": []string{"world"}, + }, + }}, + want: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + "hello": {Values: []string{"world"}}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := userAgentToPb(tt.args.ua) + assert.Equal(t, tt.want, got) + }) + } +} + func mustNewTextQuery(t testing.TB, column query.Column, value string, compare query.TextComparison) query.SearchQuery { q, err := query.NewTextQuery(column, value, compare) require.NoError(t, err) @@ -510,3 +597,73 @@ func Test_userVerificationRequirementToDomain(t *testing.T) { }) } } + +func Test_userAgentToCommand(t *testing.T) { + type args struct { + userAgent *session.UserAgent + } + tests := []struct { + name string + args args + want *domain.UserAgent + }{ + { + name: "nil", + args: args{nil}, + want: nil, + }, + { + name: "all fields", + args: args{&session.UserAgent{ + FingerprintId: gu.Ptr("fp1"), + Ip: gu.Ptr("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: map[string]*session.UserAgent_HeaderValues{ + "hello": { + Values: []string{"foo", "bar"}, + }, + }, + }}, + want: &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{ + "hello": []string{"foo", "bar"}, + }, + }, + }, + { + name: "invalid ip", + args: args{&session.UserAgent{ + FingerprintId: gu.Ptr("fp1"), + Ip: gu.Ptr("oops"), + Description: gu.Ptr("firefox"), + Header: map[string]*session.UserAgent_HeaderValues{ + "hello": { + Values: []string{"foo", "bar"}, + }, + }, + }}, + want: &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: nil, + Description: gu.Ptr("firefox"), + Header: http.Header{ + "hello": []string{"foo", "bar"}, + }, + }, + }, + { + name: "nil fields", + args: args{&session.UserAgent{}}, + want: &domain.UserAgent{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := userAgentToCommand(tt.args.userAgent) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/api/grpc/system/instance_converter.go b/internal/api/grpc/system/instance_converter.go index 4eea0a18f7..551079aec5 100644 --- a/internal/api/grpc/system/instance_converter.go +++ b/internal/api/grpc/system/instance_converter.go @@ -3,7 +3,7 @@ package system import ( "strings" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/grpc/authn" diff --git a/internal/api/oidc/auth_request.go b/internal/api/oidc/auth_request.go index 4ba077d043..56c0902b11 100644 --- a/internal/api/oidc/auth_request.go +++ b/internal/api/oidc/auth_request.go @@ -7,8 +7,8 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" http_utils "github.com/zitadel/zitadel/internal/api/http" diff --git a/internal/api/oidc/auth_request_converter.go b/internal/api/oidc/auth_request_converter.go index 5999f21e5e..8fbd18530d 100644 --- a/internal/api/oidc/auth_request_converter.go +++ b/internal/api/oidc/auth_request_converter.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/api/oidc/auth_request_converter_v2.go b/internal/api/oidc/auth_request_converter_v2.go index 9f4c0d0a1f..3a35b01578 100644 --- a/internal/api/oidc/auth_request_converter_v2.go +++ b/internal/api/oidc/auth_request_converter_v2.go @@ -3,7 +3,7 @@ package oidc import ( "time" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/command" ) diff --git a/internal/api/oidc/auth_request_integration_test.go b/internal/api/oidc/auth_request_integration_test.go index 532814bee1..5b013fe864 100644 --- a/internal/api/oidc/auth_request_integration_test.go +++ b/internal/api/oidc/auth_request_integration_test.go @@ -11,8 +11,8 @@ import ( "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" http_utils "github.com/zitadel/zitadel/internal/api/http" oidc_api "github.com/zitadel/zitadel/internal/api/oidc" @@ -103,7 +103,7 @@ func TestOPStorage_CreateAccessToken_implicit(t *testing.T) { assert.Equal(t, "state", values.Get("state")) // check id_token / claims - provider, err := Tester.CreateRelyingParty(clientID, redirectURIImplicit) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURIImplicit) require.NoError(t, err) claims, err := rp.VerifyTokens[*oidc.IDTokenClaims](context.Background(), accessToken, idToken, provider.IDTokenVerifier()) require.NoError(t, err) @@ -147,7 +147,7 @@ func TestOPStorage_CreateAccessAndRefreshTokens_code(t *testing.T) { func TestOPStorage_CreateAccessAndRefreshTokens_refresh(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -177,13 +177,13 @@ func TestOPStorage_CreateAccessAndRefreshTokens_refresh(t *testing.T) { assertIDTokenClaims(t, newTokens.IDTokenClaims, armPasskey, startTime, changeTime) // refresh with an old refresh_token must fail - _, err = rp.RefreshAccessToken(provider, tokens.RefreshToken, "", "") + _, err = rp.RefreshTokens[*oidc.IDTokenClaims](CTX, provider, tokens.RefreshToken, "", "") require.Error(t, err) } func TestOPStorage_RevokeToken_access_token(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -206,11 +206,11 @@ func TestOPStorage_RevokeToken_access_token(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke access token - err = rp.RevokeToken(provider, tokens.AccessToken, "access_token") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "access_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh grant must still work @@ -218,15 +218,15 @@ func TestOPStorage_RevokeToken_access_token(t *testing.T) { require.NoError(t, err) // revocation with the same access token must not fail (with or without hint) - err = rp.RevokeToken(provider, tokens.AccessToken, "access_token") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "access_token") require.NoError(t, err) - err = rp.RevokeToken(provider, tokens.AccessToken, "") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "") require.NoError(t, err) } func TestOPStorage_RevokeToken_access_token_invalid_token_hint_type(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -249,11 +249,11 @@ func TestOPStorage_RevokeToken_access_token_invalid_token_hint_type(t *testing.T assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke access token - err = rp.RevokeToken(provider, tokens.AccessToken, "refresh_token") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "refresh_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh grant must still work @@ -263,7 +263,7 @@ func TestOPStorage_RevokeToken_access_token_invalid_token_hint_type(t *testing.T func TestOPStorage_RevokeToken_refresh_token(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -286,11 +286,11 @@ func TestOPStorage_RevokeToken_refresh_token(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke refresh token -> invalidates also access token - err = rp.RevokeToken(provider, tokens.RefreshToken, "refresh_token") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "refresh_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh must fail @@ -298,15 +298,15 @@ func TestOPStorage_RevokeToken_refresh_token(t *testing.T) { require.Error(t, err) // revocation with the same refresh token must not fail (with or without hint) - err = rp.RevokeToken(provider, tokens.RefreshToken, "refresh_token") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "refresh_token") require.NoError(t, err) - err = rp.RevokeToken(provider, tokens.RefreshToken, "") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "") require.NoError(t, err) } func TestOPStorage_RevokeToken_refresh_token_invalid_token_type_hint(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -329,11 +329,11 @@ func TestOPStorage_RevokeToken_refresh_token_invalid_token_type_hint(t *testing. assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke refresh token even with a wrong hint - err = rp.RevokeToken(provider, tokens.RefreshToken, "access_token") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "access_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh must fail @@ -365,15 +365,15 @@ func TestOPStorage_RevokeToken_invalid_client(t *testing.T) { // simulate second client (not part of the audience) trying to revoke the token otherClientID := createClient(t) - provider, err := Tester.CreateRelyingParty(otherClientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, otherClientID, redirectURI) require.NoError(t, err) - err = rp.RevokeToken(provider, tokens.AccessToken, "") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "") require.Error(t, err) } func TestOPStorage_TerminateSession(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -396,21 +396,21 @@ func TestOPStorage_TerminateSession(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // userinfo must not fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) - postLogoutRedirect, err := rp.EndSession(provider, tokens.IDToken, logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String()) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) } func TestOPStorage_TerminateSession_refresh_grant(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -433,28 +433,28 @@ func TestOPStorage_TerminateSession_refresh_grant(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // userinfo must not fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) - postLogoutRedirect, err := rp.EndSession(provider, tokens.IDToken, logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String()) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) refreshedTokens, err := refreshTokens(t, clientID, tokens.RefreshToken) require.NoError(t, err) // userinfo must not fail - _, err = rp.Userinfo(refreshedTokens.AccessToken, refreshedTokens.TokenType, refreshedTokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, refreshedTokens.AccessToken, refreshedTokens.TokenType, refreshedTokens.IDTokenClaims.Subject, provider) require.NoError(t, err) } func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -476,12 +476,12 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) { assertTokens(t, tokens, false) assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) - postLogoutRedirect, err := rp.EndSession(provider, "", logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, "", logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, http_utils.BuildOrigin(Tester.Host(), Tester.Config.ExternalSecure)+Tester.Config.OIDC.DefaultLogoutURLV2+logoutRedirectURI+"?state=state", postLogoutRedirect.String()) // userinfo must not fail until login UI terminated session - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) // simulate termination by login UI @@ -492,12 +492,12 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) { require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) } func exchangeTokens(t testing.TB, clientID, code string) (*oidc.Tokens[*oidc.IDTokenClaims], error) { - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) codeVerifier := "codeVerifier" @@ -505,23 +505,10 @@ func exchangeTokens(t testing.TB, clientID, code string) (*oidc.Tokens[*oidc.IDT } func refreshTokens(t testing.TB, clientID, refreshToken string) (*oidc.Tokens[*oidc.IDTokenClaims], error) { - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) - tokens, err := rp.RefreshAccessToken(provider, refreshToken, "", "") - if err != nil { - return nil, err - } - idToken, _ := tokens.Extra("id_token").(string) - claims, err := rp.VerifyTokens[*oidc.IDTokenClaims](context.Background(), tokens.AccessToken, idToken, provider.IDTokenVerifier()) - if err != nil { - return nil, err - } - return &oidc.Tokens[*oidc.IDTokenClaims]{ - Token: tokens, - IDToken: idToken, - IDTokenClaims: claims, - }, nil + return rp.RefreshTokens[*oidc.IDTokenClaims](CTX, provider, refreshToken, "", "") } func assertCodeResponse(t *testing.T, callback string) string { diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 6a8dc6be6e..7c56288171 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -9,10 +9,10 @@ import ( "time" "github.com/dop251/goja" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/actions" "github.com/zitadel/zitadel/internal/actions/object" @@ -564,7 +564,7 @@ func (o *OPStorage) userinfoFlows(ctx context.Context, user *query.User, userGra apiFields, action.Script, action.Name, - append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -745,7 +745,7 @@ func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userG apiFields, action.Script, action.Name, - append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { diff --git a/internal/api/oidc/client_converter.go b/internal/api/oidc/client_converter.go index 5f3f25f759..ec208db27c 100644 --- a/internal/api/oidc/client_converter.go +++ b/internal/api/oidc/client_converter.go @@ -4,8 +4,8 @@ import ( "strings" "time" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/api/oidc/client_credentials.go b/internal/api/oidc/client_credentials.go index fda1f6c94a..3c2f272ead 100644 --- a/internal/api/oidc/client_credentials.go +++ b/internal/api/oidc/client_credentials.go @@ -3,8 +3,8 @@ package oidc import ( "time" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) type clientCredentialsRequest struct { diff --git a/internal/api/oidc/client_integration_test.go b/internal/api/oidc/client_integration_test.go index 11121a8fae..8a7f58a441 100644 --- a/internal/api/oidc/client_integration_test.go +++ b/internal/api/oidc/client_integration_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/client/rs" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rs" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/pkg/grpc/authn" "github.com/zitadel/zitadel/pkg/grpc/management" @@ -41,9 +41,9 @@ func TestOPStorage_SetUserinfoFromToken(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // test actual userinfo - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) - userinfo, err := rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + userinfo, err := rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) assertUserinfo(t, userinfo) } @@ -62,7 +62,7 @@ func TestOPStorage_SetIntrospectionFromToken(t *testing.T) { ExpirationDate: nil, }) require.NoError(t, err) - resourceServer, err := Tester.CreateResourceServer(keyResp.GetKeyDetails()) + resourceServer, err := Tester.CreateResourceServer(CTX, keyResp.GetKeyDetails()) require.NoError(t, err) scope := []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess} @@ -87,7 +87,7 @@ func TestOPStorage_SetIntrospectionFromToken(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // test actual introspection - introspection, err := rs.Introspect(context.Background(), resourceServer, tokens.AccessToken) + introspection, err := rs.Introspect[*oidc.IntrospectionResponse](context.Background(), resourceServer, tokens.AccessToken) require.NoError(t, err) assertIntrospection(t, introspection, Tester.OIDCIssuer(), app.GetClientId(), diff --git a/internal/api/oidc/device_auth.go b/internal/api/oidc/device_auth.go index 7eee06096a..80298e8afd 100644 --- a/internal/api/oidc/device_auth.go +++ b/internal/api/oidc/device_auth.go @@ -5,8 +5,8 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/ui/login" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/api/oidc/jwt-profile.go b/internal/api/oidc/jwt-profile.go index 47805783c9..e2592a5734 100644 --- a/internal/api/oidc/jwt-profile.go +++ b/internal/api/oidc/jwt-profile.go @@ -3,8 +3,8 @@ package oidc import ( "context" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" diff --git a/internal/api/oidc/key.go b/internal/api/oidc/key.go index 82884101d6..3a2a6ae32c 100644 --- a/internal/api/oidc/key.go +++ b/internal/api/oidc/key.go @@ -5,9 +5,9 @@ import ( "fmt" "time" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/api/oidc/oidc_integration_test.go b/internal/api/oidc/oidc_integration_test.go index 1a1516040d..7738e3d615 100644 --- a/internal/api/oidc/oidc_integration_test.go +++ b/internal/api/oidc/oidc_integration_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "google.golang.org/grpc/metadata" "github.com/zitadel/zitadel/internal/domain" @@ -216,7 +216,7 @@ func Test_ZITADEL_API_inactive_access_token(t *testing.T) { func Test_ZITADEL_API_terminated_session(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -245,7 +245,7 @@ func Test_ZITADEL_API_terminated_session(t *testing.T) { require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId()) // refresh token - postLogoutRedirect, err := rp.EndSession(provider, tokens.IDToken, logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String()) @@ -271,13 +271,13 @@ func createImplicitClient(t testing.TB) string { } func createAuthRequest(t testing.TB, clientID, redirectURI string, scope ...string) string { - redURL, err := Tester.CreateOIDCAuthRequest(clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) + redURL, err := Tester.CreateOIDCAuthRequest(CTX, clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) require.NoError(t, err) return redURL } func createAuthRequestImplicit(t testing.TB, clientID, redirectURI string, scope ...string) string { - redURL, err := Tester.CreateOIDCAuthRequestImplicit(clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) + redURL, err := Tester.CreateOIDCAuthRequestImplicit(CTX, clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) require.NoError(t, err) return redURL } diff --git a/internal/api/oidc/op.go b/internal/api/oidc/op.go index da96302e60..c165b117b9 100644 --- a/internal/api/oidc/op.go +++ b/internal/api/oidc/op.go @@ -7,8 +7,8 @@ import ( "time" "github.com/rakyll/statik/fs" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/assets" diff --git a/internal/api/saml/certificate.go b/internal/api/saml/certificate.go index d2bf3bf3e2..6dd02e1bc2 100644 --- a/internal/api/saml/certificate.go +++ b/internal/api/saml/certificate.go @@ -5,9 +5,9 @@ import ( "fmt" "time" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" "github.com/zitadel/saml/pkg/provider/key" - "gopkg.in/square/go-jose.v2" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/api/ui/console/console.go b/internal/api/ui/console/console.go index 7e00605b7b..6f632136f0 100644 --- a/internal/api/ui/console/console.go +++ b/internal/api/ui/console/console.go @@ -15,7 +15,7 @@ import ( "github.com/gorilla/mux" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/cmd/build" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/api/ui/login/custom_action.go b/internal/api/ui/login/custom_action.go index 516f7bc3d0..f7287dd1d2 100644 --- a/internal/api/ui/login/custom_action.go +++ b/internal/api/ui/login/custom_action.go @@ -7,7 +7,7 @@ import ( "github.com/dop251/goja" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/actions" @@ -133,7 +133,7 @@ func (l *Login) runPostExternalAuthenticationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -206,7 +206,7 @@ func (l *Login) runPostInternalAuthenticationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -307,7 +307,7 @@ func (l *Login) runPreCreationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -365,7 +365,7 @@ func (l *Login) runPostCreationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index c9cc94203f..d9c8c04fc0 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -7,8 +7,8 @@ import ( "github.com/crewjam/saml/samlsp" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/api/ui/login/jwt_handler.go b/internal/api/ui/login/jwt_handler.go index 51b0795843..aa7a4466dc 100644 --- a/internal/api/ui/login/jwt_handler.go +++ b/internal/api/ui/login/jwt_handler.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" http_util "github.com/zitadel/zitadel/internal/api/http" diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index d67b2dcc7b..42a9ba350b 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -7,10 +7,10 @@ import ( "strings" "time" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" http_util "github.com/zitadel/zitadel/internal/api/http" @@ -274,7 +274,7 @@ func (repo *TokenVerifierRepo) getTokenIDAndSubject(ctx context.Context, accessT return splitToken[0], splitToken[1], true } -func (repo *TokenVerifierRepo) jwtTokenVerifier(ctx context.Context) op.AccessTokenVerifier { +func (repo *TokenVerifierRepo) jwtTokenVerifier(ctx context.Context) *op.AccessTokenVerifier { keySet := &openIDKeySet{repo.Query} issuer := http_util.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), repo.ExternalSecure) return op.NewAccessTokenVerifier(issuer, keySet) diff --git a/internal/command/auth_request_test.go b/internal/command/auth_request_test.go index 0c1bb1cf31..183e1b57c1 100644 --- a/internal/command/auth_request_test.go +++ b/internal/command/auth_request_test.go @@ -2,6 +2,8 @@ package command import ( "context" + "net" + "net/http" "testing" "time" @@ -354,7 +356,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { @@ -397,7 +407,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { @@ -440,8 +458,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate), - ), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, "userID", testNow), @@ -517,8 +542,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate), - ), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, "userID", testNow), diff --git a/internal/command/idp_intent.go b/internal/command/idp_intent.go index 265293a3a8..edf28ef460 100644 --- a/internal/command/idp_intent.go +++ b/internal/command/idp_intent.go @@ -9,7 +9,7 @@ import ( "github.com/crewjam/saml" "github.com/crewjam/saml/samlsp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go index 4f5b35c339..400b0faf0e 100644 --- a/internal/command/idp_intent_test.go +++ b/internal/command/idp_intent_test.go @@ -9,7 +9,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index b4d932fb2b..e9949fb406 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -7,7 +7,7 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rp" "golang.org/x/oauth2" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index 3e45d82666..8989f4c92b 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -8,7 +8,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/command/oidc_session_test.go b/internal/command/oidc_session_test.go index ab850aeea1..a517814e00 100644 --- a/internal/command/oidc_session_test.go +++ b/internal/command/oidc_session_test.go @@ -2,6 +2,8 @@ package command import ( "context" + "net" + "net/http" "testing" "time" @@ -163,7 +165,15 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + ), ), eventFromEventPusher( session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, @@ -356,7 +366,15 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + ), ), eventFromEventPusher( session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index d6eef4a399..8f966e7402 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -8,7 +8,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/command/org_test.go b/internal/command/org_test.go index 6db8654d90..6f00765bcc 100644 --- a/internal/command/org_test.go +++ b/internal/command/org_test.go @@ -7,7 +7,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/command/session.go b/internal/command/session.go index a58c5d2d3a..caf3056f76 100644 --- a/internal/command/session.go +++ b/internal/command/session.go @@ -166,8 +166,8 @@ func (s *SessionCommands) Exec(ctx context.Context) error { return nil } -func (s *SessionCommands) Start(ctx context.Context) { - s.eventCommands = append(s.eventCommands, session.NewAddedEvent(ctx, s.sessionWriteModel.aggregate)) +func (s *SessionCommands) Start(ctx context.Context, userAgent *domain.UserAgent) { + s.eventCommands = append(s.eventCommands, session.NewAddedEvent(ctx, s.sessionWriteModel.aggregate, userAgent)) } func (s *SessionCommands) UserChecked(ctx context.Context, userID string, checkedAt time.Time) error { @@ -280,7 +280,7 @@ func (s *SessionCommands) commands(ctx context.Context) (string, []eventstore.Co return token, s.eventCommands, nil } -func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, metadata map[string][]byte) (set *SessionChanged, err error) { +func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, metadata map[string][]byte, userAgent *domain.UserAgent) (set *SessionChanged, err error) { sessionID, err := c.idGenerator.Next() if err != nil { return nil, err @@ -291,7 +291,7 @@ func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, met return nil, err } cmd := c.NewSessionCommands(cmds, sessionWriteModel) - cmd.Start(ctx) + cmd.Start(ctx, userAgent) return c.updateSession(ctx, cmd, metadata) } diff --git a/internal/command/session_test.go b/internal/command/session_test.go index e478792e1e..7e443a4c53 100644 --- a/internal/command/session_test.go +++ b/internal/command/session_test.go @@ -3,10 +3,13 @@ package command import ( "context" "io" + "net" + "net/http" "testing" "time" "github.com/golang/mock/gomock" + "github.com/muhlemmer/gu" "github.com/pquerna/otp/totp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -145,9 +148,10 @@ func TestCommands_CreateSession(t *testing.T) { tokenCreator func(sessionID string) (string, string, error) } type args struct { - ctx context.Context - checks []SessionCommand - metadata map[string][]byte + ctx context.Context + checks []SessionCommand + metadata map[string][]byte + userAgent *domain.UserAgent } type res struct { want *SessionChanged @@ -200,11 +204,25 @@ func TestCommands_CreateSession(t *testing.T) { }, args{ ctx: authz.NewMockContext("", "org1", ""), + userAgent: &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, }, []expect{ expectFilter(), expectPush( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + ), session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID", ), @@ -227,7 +245,7 @@ func TestCommands_CreateSession(t *testing.T) { idGenerator: tt.fields.idGenerator, sessionTokenCreator: tt.fields.tokenCreator, } - got, err := c.CreateSession(tt.args.ctx, tt.args.checks, tt.args.metadata) + got, err := c.CreateSession(tt.args.ctx, tt.args.checks, tt.args.metadata, tt.args.userAgent) require.ErrorIs(t, err, tt.res.err) assert.Equal(t, tt.res.want, got) }) @@ -276,7 +294,15 @@ func TestCommands_UpdateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -301,7 +327,15 @@ func TestCommands_UpdateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -866,7 +900,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -891,7 +933,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -920,7 +970,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID"), @@ -950,7 +1008,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID"), diff --git a/internal/command/user_human_refresh_token_test.go b/internal/command/user_human_refresh_token_test.go index 6ad7f68654..c623c737b0 100644 --- a/internal/command/user_human_refresh_token_test.go +++ b/internal/command/user_human_refresh_token_test.go @@ -8,7 +8,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/crypto/passwap.go b/internal/crypto/passwap.go index a5a293a449..479d5731e4 100644 --- a/internal/crypto/passwap.go +++ b/internal/crypto/passwap.go @@ -147,7 +147,7 @@ func (c *HasherConfig) buildHasher() (hasher passwap.Hasher, prefixes []string, func (c *HasherConfig) decodeParams(dst any) error { decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - ErrorUnused: true, + ErrorUnused: false, ErrorUnset: true, Result: dst, }) diff --git a/internal/crypto/passwap_test.go b/internal/crypto/passwap_test.go index b557ca4a5c..0538ac631a 100644 --- a/internal/crypto/passwap_test.go +++ b/internal/crypto/passwap_test.go @@ -379,7 +379,10 @@ func TestHasherConfig_decodeParams(t *testing.T) { "b": 2, "c": 3, }, - wantErr: true, + want: dst{ + A: 1, + B: 2, + }, }, { name: "unset", diff --git a/internal/domain/user_agent.go b/internal/domain/user_agent.go new file mode 100644 index 0000000000..ca72c6bec4 --- /dev/null +++ b/internal/domain/user_agent.go @@ -0,0 +1,17 @@ +package domain + +import ( + "net" + httplib "net/http" +) + +type UserAgent struct { + FingerprintID *string `json:"fingerprint_id,omitempty"` + IP net.IP `json:"ip,omitempty"` + Description *string `json:"description,omitempty"` + Header httplib.Header `json:"header,omitempty"` +} + +func (ua UserAgent) IsEmpty() bool { + return ua.FingerprintID == nil && len(ua.IP) == 0 && ua.Description == nil && ua.Header == nil +} diff --git a/internal/idp/providers/apple/apple.go b/internal/idp/providers/apple/apple.go index 9463cb61bf..9664e006c8 100644 --- a/internal/idp/providers/apple/apple.go +++ b/internal/idp/providers/apple/apple.go @@ -5,9 +5,9 @@ import ( "encoding/pem" "time" - "github.com/zitadel/oidc/v2/pkg/crypto" - openid "github.com/zitadel/oidc/v2/pkg/oidc" - "gopkg.in/square/go-jose.v2" + "github.com/go-jose/go-jose/v3" + "github.com/zitadel/oidc/v3/pkg/crypto" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/apple/session.go b/internal/idp/providers/apple/session.go index ae629f9573..5e9143f050 100644 --- a/internal/idp/providers/apple/session.go +++ b/internal/idp/providers/apple/session.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/apple/session_test.go b/internal/idp/providers/apple/session_test.go index 207f219813..3c1ab59763 100644 --- a/internal/idp/providers/apple/session_test.go +++ b/internal/idp/providers/apple/session_test.go @@ -9,7 +9,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/azuread/azuread.go b/internal/idp/providers/azuread/azuread.go index 244f383e1c..46445a3977 100644 --- a/internal/idp/providers/azuread/azuread.go +++ b/internal/idp/providers/azuread/azuread.go @@ -3,7 +3,7 @@ package azuread import ( "fmt" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/azuread/azuread_test.go b/internal/idp/providers/azuread/azuread_test.go index 3febb43f95..122a70bb07 100644 --- a/internal/idp/providers/azuread/azuread_test.go +++ b/internal/idp/providers/azuread/azuread_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oauth" diff --git a/internal/idp/providers/azuread/session.go b/internal/idp/providers/azuread/session.go index 5bc7bb84c9..698cdad198 100644 --- a/internal/idp/providers/azuread/session.go +++ b/internal/idp/providers/azuread/session.go @@ -3,8 +3,8 @@ package azuread import ( "net/http" - httphelper "github.com/zitadel/oidc/v2/pkg/http" - "github.com/zitadel/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp/providers/oauth" ) diff --git a/internal/idp/providers/azuread/session_test.go b/internal/idp/providers/azuread/session_test.go index 531d909b92..f68c4cc7d7 100644 --- a/internal/idp/providers/azuread/session_test.go +++ b/internal/idp/providers/azuread/session_test.go @@ -10,7 +10,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/github/session_test.go b/internal/idp/providers/github/session_test.go index 2ad9f449fc..247ef35c68 100644 --- a/internal/idp/providers/github/session_test.go +++ b/internal/idp/providers/github/session_test.go @@ -10,7 +10,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/gitlab/gitlab.go b/internal/idp/providers/gitlab/gitlab.go index 1bb02302f3..76e9a74b40 100644 --- a/internal/idp/providers/gitlab/gitlab.go +++ b/internal/idp/providers/gitlab/gitlab.go @@ -1,7 +1,7 @@ package gitlab import ( - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/gitlab/session_test.go b/internal/idp/providers/gitlab/session_test.go index de59044326..0853f3ce3e 100644 --- a/internal/idp/providers/gitlab/session_test.go +++ b/internal/idp/providers/gitlab/session_test.go @@ -9,8 +9,8 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/google/google.go b/internal/idp/providers/google/google.go index 7036d4e101..221f2b61ae 100644 --- a/internal/idp/providers/google/google.go +++ b/internal/idp/providers/google/google.go @@ -1,7 +1,7 @@ package google import ( - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/google/session_test.go b/internal/idp/providers/google/session_test.go index d3edde0ea3..1915adf1bc 100644 --- a/internal/idp/providers/google/session_test.go +++ b/internal/idp/providers/google/session_test.go @@ -9,8 +9,8 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/jwt/session.go b/internal/idp/providers/jwt/session.go index be3ffc0531..54fcc039eb 100644 --- a/internal/idp/providers/jwt/session.go +++ b/internal/idp/providers/jwt/session.go @@ -8,8 +8,8 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/idp/providers/jwt/session_test.go b/internal/idp/providers/jwt/session_test.go index ae79ed22e5..3a8210aec8 100644 --- a/internal/idp/providers/jwt/session_test.go +++ b/internal/idp/providers/jwt/session_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" + "github.com/go-jose/go-jose/v3" "github.com/golang/mock/gomock" "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" - "gopkg.in/square/go-jose.v2" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/idp/providers/oauth/oauth2.go b/internal/idp/providers/oauth/oauth2.go index a31e9d4c26..ad8cbbd45b 100644 --- a/internal/idp/providers/oauth/oauth2.go +++ b/internal/idp/providers/oauth/oauth2.go @@ -3,8 +3,8 @@ package oauth import ( "context" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "github.com/zitadel/zitadel/internal/idp" diff --git a/internal/idp/providers/oauth/oauth2_test.go b/internal/idp/providers/oauth/oauth2_test.go index 5fdfbc2185..814a7ac9c2 100644 --- a/internal/idp/providers/oauth/oauth2_test.go +++ b/internal/idp/providers/oauth/oauth2_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rp" "golang.org/x/oauth2" "github.com/zitadel/zitadel/internal/idp" diff --git a/internal/idp/providers/oauth/session.go b/internal/idp/providers/oauth/session.go index eb9ab49efe..065ad3b213 100644 --- a/internal/idp/providers/oauth/session.go +++ b/internal/idp/providers/oauth/session.go @@ -5,9 +5,9 @@ import ( "errors" "net/http" - "github.com/zitadel/oidc/v2/pkg/client/rp" - httphelper "github.com/zitadel/oidc/v2/pkg/http" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" ) diff --git a/internal/idp/providers/oauth/session_test.go b/internal/idp/providers/oauth/session_test.go index 9901ea11c3..ce6fa3f10f 100644 --- a/internal/idp/providers/oauth/session_test.go +++ b/internal/idp/providers/oauth/session_test.go @@ -9,7 +9,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/oidc/oidc.go b/internal/idp/providers/oidc/oidc.go index aab5255488..30d4d11abf 100644 --- a/internal/idp/providers/oidc/oidc.go +++ b/internal/idp/providers/oidc/oidc.go @@ -3,8 +3,8 @@ package oidc import ( "context" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" ) @@ -100,7 +100,7 @@ func New(name, issuer, clientID, clientSecret, redirectURI string, scopes []stri for _, option := range options { option(provider) } - provider.RelyingParty, err = rp.NewRelyingPartyOIDC(issuer, clientID, clientSecret, redirectURI, setDefaultScope(scopes), provider.options...) + provider.RelyingParty, err = rp.NewRelyingPartyOIDC(context.TODO(), issuer, clientID, clientSecret, redirectURI, setDefaultScope(scopes), provider.options...) if err != nil { return nil, err } diff --git a/internal/idp/providers/oidc/oidc_test.go b/internal/idp/providers/oidc/oidc_test.go index bbe08155c8..d510bf15c2 100644 --- a/internal/idp/providers/oidc/oidc_test.go +++ b/internal/idp/providers/oidc/oidc_test.go @@ -7,8 +7,8 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" ) diff --git a/internal/idp/providers/oidc/session.go b/internal/idp/providers/oidc/session.go index 366e42643a..bb44fb1146 100644 --- a/internal/idp/providers/oidc/session.go +++ b/internal/idp/providers/oidc/session.go @@ -4,8 +4,8 @@ import ( "context" "errors" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/domain" @@ -38,7 +38,7 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) { return nil, err } } - info, err := rp.Userinfo( + info, err := rp.Userinfo[*oidc.UserInfo](ctx, s.Tokens.AccessToken, s.Tokens.TokenType, s.Tokens.IDTokenClaims.GetSubject(), diff --git a/internal/idp/providers/oidc/session_test.go b/internal/idp/providers/oidc/session_test.go index afaa358042..200dac8fc4 100644 --- a/internal/idp/providers/oidc/session_test.go +++ b/internal/idp/providers/oidc/session_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" + "github.com/go-jose/go-jose/v3" "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" - "gopkg.in/square/go-jose.v2" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/integration/client.go b/internal/integration/client.go index 71bc9805c4..98a577be14 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -9,7 +9,7 @@ import ( crewjam_saml "github.com/crewjam/saml" "github.com/stretchr/testify/require" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" "google.golang.org/grpc" diff --git a/internal/integration/integration.go b/internal/integration/integration.go index 3e270b1d72..61d7f9d90d 100644 --- a/internal/integration/integration.go +++ b/internal/integration/integration.go @@ -19,8 +19,8 @@ import ( "github.com/spf13/viper" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/oidc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" diff --git a/internal/integration/oidc.go b/internal/integration/oidc.go index ad1a44de32..b6edcd3aea 100644 --- a/internal/integration/oidc.go +++ b/internal/integration/oidc.go @@ -8,10 +8,10 @@ import ( "strings" "time" - "github.com/zitadel/oidc/v2/pkg/client" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/client/rs" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rs" + "github.com/zitadel/oidc/v3/pkg/oidc" http_util "github.com/zitadel/zitadel/internal/api/http" oidc_internal "github.com/zitadel/zitadel/internal/api/oidc" @@ -83,8 +83,8 @@ func (s *Tester) CreateAPIClient(ctx context.Context, projectID string) (*manage }) } -func (s *Tester) CreateOIDCAuthRequest(clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { - provider, err := s.CreateRelyingParty(clientID, redirectURI, scope...) +func (s *Tester) CreateOIDCAuthRequest(ctx context.Context, clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { + provider, err := s.CreateRelyingParty(ctx, clientID, redirectURI, scope...) if err != nil { return "", err } @@ -110,8 +110,8 @@ func (s *Tester) CreateOIDCAuthRequest(clientID, loginClient, redirectURI string return strings.TrimPrefix(loc.String(), prefixWithHost), nil } -func (s *Tester) CreateOIDCAuthRequestImplicit(clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { - provider, err := s.CreateRelyingParty(clientID, redirectURI, scope...) +func (s *Tester) CreateOIDCAuthRequestImplicit(ctx context.Context, clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { + provider, err := s.CreateRelyingParty(ctx, clientID, redirectURI, scope...) if err != nil { return "", err } @@ -146,12 +146,12 @@ func (s *Tester) OIDCIssuer() string { return http_util.BuildHTTP(s.Config.ExternalDomain, s.Config.Port, s.Config.ExternalSecure) } -func (s *Tester) CreateRelyingParty(clientID, redirectURI string, scope ...string) (rp.RelyingParty, error) { +func (s *Tester) CreateRelyingParty(ctx context.Context, clientID, redirectURI string, scope ...string) (rp.RelyingParty, error) { if len(scope) == 0 { scope = []string{oidc.ScopeOpenID} } loginClient := &http.Client{Transport: &loginRoundTripper{http.DefaultTransport}} - return rp.NewRelyingPartyOIDC(s.OIDCIssuer(), clientID, "", redirectURI, scope, rp.WithHTTPClient(loginClient)) + return rp.NewRelyingPartyOIDC(ctx, s.OIDCIssuer(), clientID, "", redirectURI, scope, rp.WithHTTPClient(loginClient)) } type loginRoundTripper struct { @@ -163,12 +163,12 @@ func (c *loginRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) return c.RoundTripper.RoundTrip(req) } -func (s *Tester) CreateResourceServer(keyFileData []byte) (rs.ResourceServer, error) { +func (s *Tester) CreateResourceServer(ctx context.Context, keyFileData []byte) (rs.ResourceServer, error) { keyFile, err := client.ConfigFromKeyFileData(keyFileData) if err != nil { return nil, err } - return rs.NewResourceServerJWTProfile(s.OIDCIssuer(), keyFile.ClientID, keyFile.KeyID, []byte(keyFile.Key)) + return rs.NewResourceServerJWTProfile(ctx, s.OIDCIssuer(), keyFile.ClientID, keyFile.KeyID, []byte(keyFile.Key)) } func GetRequest(url string, headers map[string]string) (*http.Request, error) { diff --git a/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl b/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl index bc0123c750..adb71c42ff 100644 --- a/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl +++ b/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl @@ -17,8 +17,8 @@ var {{.ServiceName}}_AuthMethods = authz.MethodMapping { } {{ range $m := .AuthContext}} -func (r *{{ $m.Name }}) OrganisationFromRequest() *middleware.Organisation { - return &middleware.Organisation{ +func (r *{{ $m.Name }}) OrganizationFromRequest() *middleware.Organization { + return &middleware.Organization{ ID: r{{$m.OrgMethod}}.GetOrgId(), Domain: r{{$m.OrgMethod}}.GetOrgDomain(), } diff --git a/internal/query/prepare_test.go b/internal/query/prepare_test.go index 242b387408..c9770c0dd1 100644 --- a/internal/query/prepare_test.go +++ b/internal/query/prepare_test.go @@ -54,7 +54,7 @@ func assertPrepare(t *testing.T, prepareFunc, expectedObject interface{}, sqlExp } return isErr(err) } - object, ok, didScan := execScan(&database.DB{DB: client}, builder, scan, errCheck) + object, ok, didScan := execScan(t, &database.DB{DB: client}, builder, scan, errCheck) if !ok { t.Error(object) return false @@ -168,7 +168,7 @@ var ( selectBuilderType = reflect.TypeOf(sq.SelectBuilder{}) ) -func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, errCheck checkErr) (object interface{}, ok bool, didScan bool) { +func execScan(t testing.TB, client *database.DB, builder sq.SelectBuilder, scan interface{}, errCheck checkErr) (object interface{}, ok bool, didScan bool) { scanType := reflect.TypeOf(scan) err := validateScan(scanType) if err != nil { @@ -177,7 +177,7 @@ func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, e stmt, args, err := builder.ToSql() if err != nil { - return fmt.Errorf("unexpeted error from sql builder: %w", err), false, false + return fmt.Errorf("unexpected error from sql builder: %w", err), false, false } //resultSet represents *sql.Row or *sql.Rows, @@ -199,6 +199,9 @@ func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, e // if scan(*sql.Row)... } else if scanType.In(0).AssignableTo(rowType) { err = client.QueryRow(func(r *sql.Row) error { + if r.Err() != nil { + return r.Err() + } didScan = true res = reflect.ValueOf(scan).Call([]reflect.Value{reflect.ValueOf(r)}) if err, ok := res[1].Interface().(error); ok { @@ -213,6 +216,9 @@ func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, e if err != nil { err, ok := errCheck(err) + if !ok { + t.Fatal(err) + } if didScan { return res[0].Interface(), ok, didScan } diff --git a/internal/query/projection/session.go b/internal/query/projection/session.go index 816969d662..5bfc5dd8c9 100644 --- a/internal/query/projection/session.go +++ b/internal/query/projection/session.go @@ -14,27 +14,31 @@ import ( ) const ( - SessionsProjectionTable = "projections.sessions5" + SessionsProjectionTable = "projections.sessions6" - SessionColumnID = "id" - SessionColumnCreationDate = "creation_date" - SessionColumnChangeDate = "change_date" - SessionColumnSequence = "sequence" - SessionColumnState = "state" - SessionColumnResourceOwner = "resource_owner" - SessionColumnInstanceID = "instance_id" - SessionColumnCreator = "creator" - SessionColumnUserID = "user_id" - SessionColumnUserCheckedAt = "user_checked_at" - SessionColumnPasswordCheckedAt = "password_checked_at" - SessionColumnIntentCheckedAt = "intent_checked_at" - SessionColumnWebAuthNCheckedAt = "webauthn_checked_at" - SessionColumnWebAuthNUserVerified = "webauthn_user_verified" - SessionColumnTOTPCheckedAt = "totp_checked_at" - SessionColumnOTPSMSCheckedAt = "otp_sms_checked_at" - SessionColumnOTPEmailCheckedAt = "otp_email_checked_at" - SessionColumnMetadata = "metadata" - SessionColumnTokenID = "token_id" + SessionColumnID = "id" + SessionColumnCreationDate = "creation_date" + SessionColumnChangeDate = "change_date" + SessionColumnSequence = "sequence" + SessionColumnState = "state" + SessionColumnResourceOwner = "resource_owner" + SessionColumnInstanceID = "instance_id" + SessionColumnCreator = "creator" + SessionColumnUserID = "user_id" + SessionColumnUserCheckedAt = "user_checked_at" + SessionColumnPasswordCheckedAt = "password_checked_at" + SessionColumnIntentCheckedAt = "intent_checked_at" + SessionColumnWebAuthNCheckedAt = "webauthn_checked_at" + SessionColumnWebAuthNUserVerified = "webauthn_user_verified" + SessionColumnTOTPCheckedAt = "totp_checked_at" + SessionColumnOTPSMSCheckedAt = "otp_sms_checked_at" + SessionColumnOTPEmailCheckedAt = "otp_email_checked_at" + SessionColumnMetadata = "metadata" + SessionColumnTokenID = "token_id" + SessionColumnUserAgentFingerprintID = "user_agent_fingerprint_id" + SessionColumnUserAgentIP = "user_agent_ip" + SessionColumnUserAgentDescription = "user_agent_description" + SessionColumnUserAgentHeader = "user_agent_header" ) type sessionProjection struct{} @@ -69,8 +73,16 @@ func (*sessionProjection) Init() *old_handler.Check { handler.NewColumn(SessionColumnOTPEmailCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), handler.NewColumn(SessionColumnMetadata, handler.ColumnTypeJSONB, handler.Nullable()), handler.NewColumn(SessionColumnTokenID, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentFingerprintID, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentIP, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentDescription, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentHeader, handler.ColumnTypeJSONB, handler.Nullable()), }, handler.NewPrimaryKey(SessionColumnInstanceID, SessionColumnID), + handler.WithIndex(handler.NewIndex( + SessionColumnUserAgentFingerprintID+"_idx", + []string{SessionColumnUserAgentFingerprintID}, + )), ), ) } @@ -153,19 +165,35 @@ func (p *sessionProjection) reduceSessionAdded(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sfrgf", "reduce.wrong.event.type %s", session.AddedType) } - return handler.NewCreateStatement( - e, - []handler.Column{ - handler.NewCol(SessionColumnID, e.Aggregate().ID), - handler.NewCol(SessionColumnInstanceID, e.Aggregate().InstanceID), - handler.NewCol(SessionColumnCreationDate, e.CreationDate()), - handler.NewCol(SessionColumnChangeDate, e.CreationDate()), - handler.NewCol(SessionColumnResourceOwner, e.Aggregate().ResourceOwner), - handler.NewCol(SessionColumnState, domain.SessionStateActive), - handler.NewCol(SessionColumnSequence, e.Sequence()), - handler.NewCol(SessionColumnCreator, e.User), - }, - ), nil + cols := make([]handler.Column, 0, 12) + cols = append(cols, + handler.NewCol(SessionColumnID, e.Aggregate().ID), + handler.NewCol(SessionColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCol(SessionColumnCreationDate, e.CreationDate()), + handler.NewCol(SessionColumnChangeDate, e.CreationDate()), + handler.NewCol(SessionColumnResourceOwner, e.Aggregate().ResourceOwner), + handler.NewCol(SessionColumnState, domain.SessionStateActive), + handler.NewCol(SessionColumnSequence, e.Sequence()), + handler.NewCol(SessionColumnCreator, e.User), + ) + if e.UserAgent != nil { + cols = append(cols, + handler.NewCol(SessionColumnUserAgentFingerprintID, e.UserAgent.FingerprintID), + handler.NewCol(SessionColumnUserAgentDescription, e.UserAgent.Description), + ) + if e.UserAgent.IP != nil { + cols = append(cols, + handler.NewCol(SessionColumnUserAgentIP, e.UserAgent.IP.String()), + ) + } + if e.UserAgent.Header != nil { + cols = append(cols, + handler.NewJSONCol(SessionColumnUserAgentHeader, e.UserAgent.Header), + ) + } + } + + return handler.NewCreateStatement(e, cols), nil } func (p *sessionProjection) reduceUserChecked(event eventstore.Event) (*handler.Statement, error) { diff --git a/internal/query/projection/session_test.go b/internal/query/projection/session_test.go index 607b028ff4..b27b443994 100644 --- a/internal/query/projection/session_test.go +++ b/internal/query/projection/session_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + "github.com/muhlemmer/gu" + "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -30,7 +32,15 @@ func TestSessionProjection_reduces(t *testing.T) { session.AddedType, session.AggregateType, []byte(`{ - "domain": "domain" + "domain": "domain", + "user_agent": { + "fingerprint_id": "fp1", + "ip": "1.2.3.4", + "description": "firefox", + "header": { + "foo": ["bar"] + } + } }`), ), session.AddedEventMapper), }, @@ -41,7 +51,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.sessions5 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.sessions6 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator, user_agent_fingerprint_id, user_agent_description, user_agent_ip, user_agent_header) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -51,6 +61,10 @@ func TestSessionProjection_reduces(t *testing.T) { domain.SessionStateActive, uint64(15), "editor-user", + gu.Ptr("fp1"), + gu.Ptr("firefox"), + "1.2.3.4", + []byte(`{"foo":["bar"]}`), }, }, }, @@ -76,7 +90,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -108,7 +122,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -140,7 +154,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -172,7 +186,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -203,7 +217,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -234,7 +248,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -267,7 +281,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -298,7 +312,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.sessions5 WHERE (id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.sessions6 WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -325,7 +339,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.sessions5 WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.sessions6 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, @@ -355,7 +369,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET password_checked_at = $1 WHERE (user_id = $2) AND (password_checked_at < $3)", + expectedStmt: "UPDATE projections.sessions6 SET password_checked_at = $1 WHERE (user_id = $2) AND (password_checked_at < $3)", expectedArgs: []interface{}{ nil, "agg-id", diff --git a/internal/query/session.go b/internal/query/session.go index bcc87a3031..c2921d235d 100644 --- a/internal/query/session.go +++ b/internal/query/session.go @@ -4,6 +4,8 @@ import ( "context" "database/sql" errs "errors" + "net" + "net/http" "time" sq "github.com/Masterminds/squirrel" @@ -41,6 +43,7 @@ type Session struct { OTPSMSFactor SessionOTPFactor OTPEmailFactor SessionOTPFactor Metadata map[string][]byte + UserAgent domain.UserAgent } type SessionUserFactor struct { @@ -166,6 +169,22 @@ var ( name: projection.SessionColumnTokenID, table: sessionsTable, } + SessionColumnUserAgentFingerprintID = Column{ + name: projection.SessionColumnUserAgentFingerprintID, + table: sessionsTable, + } + SessionColumnUserAgentIP = Column{ + name: projection.SessionColumnUserAgentIP, + table: sessionsTable, + } + SessionColumnUserAgentDescription = Column{ + name: projection.SessionColumnUserAgentDescription, + table: sessionsTable, + } + SessionColumnUserAgentHeader = Column{ + name: projection.SessionColumnUserAgentHeader, + table: sessionsTable, + } ) func (q *Queries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, sessionToken string) (session *Session, err error) { @@ -265,6 +284,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil SessionColumnOTPEmailCheckedAt.identifier(), SessionColumnMetadata.identifier(), SessionColumnToken.identifier(), + SessionColumnUserAgentFingerprintID.identifier(), + SessionColumnUserAgentIP.identifier(), + SessionColumnUserAgentDescription.identifier(), + SessionColumnUserAgentHeader.identifier(), ).From(sessionsTable.identifier()). LeftJoin(join(LoginNameUserIDCol, SessionColumnUserID)). LeftJoin(join(HumanUserIDCol, SessionColumnUserID)). @@ -287,6 +310,8 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil otpEmailCheckedAt sql.NullTime metadata database.Map[[]byte] token sql.NullString + userAgentIP sql.NullString + userAgentHeader database.Map[[]string] ) err := row.Scan( @@ -311,6 +336,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil &otpEmailCheckedAt, &metadata, &token, + &session.UserAgent.FingerprintID, + &userAgentIP, + &session.UserAgent.Description, + &userAgentHeader, ) if err != nil { @@ -333,7 +362,11 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil session.OTPSMSFactor.OTPCheckedAt = otpSMSCheckedAt.Time session.OTPEmailFactor.OTPCheckedAt = otpEmailCheckedAt.Time session.Metadata = metadata + session.UserAgent.Header = http.Header(userAgentHeader) + if userAgentIP.Valid { + session.UserAgent.IP = net.ParseIP(userAgentIP.String) + } return session, token.String, nil } } diff --git a/internal/query/sessions_test.go b/internal/query/sessions_test.go index fa5209bdd3..1bae095ec6 100644 --- a/internal/query/sessions_test.go +++ b/internal/query/sessions_test.go @@ -6,10 +6,13 @@ import ( "database/sql/driver" "errors" "fmt" + "net" + "net/http" "regexp" "testing" sq "github.com/Masterminds/squirrel" + "github.com/muhlemmer/gu" "github.com/stretchr/testify/require" "github.com/zitadel/zitadel/internal/domain" @@ -17,57 +20,61 @@ import ( ) var ( - expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions5.id,` + - ` projections.sessions5.creation_date,` + - ` projections.sessions5.change_date,` + - ` projections.sessions5.sequence,` + - ` projections.sessions5.state,` + - ` projections.sessions5.resource_owner,` + - ` projections.sessions5.creator,` + - ` projections.sessions5.user_id,` + - ` projections.sessions5.user_checked_at,` + + expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions6.id,` + + ` projections.sessions6.creation_date,` + + ` projections.sessions6.change_date,` + + ` projections.sessions6.sequence,` + + ` projections.sessions6.state,` + + ` projections.sessions6.resource_owner,` + + ` projections.sessions6.creator,` + + ` projections.sessions6.user_id,` + + ` projections.sessions6.user_checked_at,` + ` projections.login_names2.login_name,` + ` projections.users8_humans.display_name,` + ` projections.users8.resource_owner,` + - ` projections.sessions5.password_checked_at,` + - ` projections.sessions5.intent_checked_at,` + - ` projections.sessions5.webauthn_checked_at,` + - ` projections.sessions5.webauthn_user_verified,` + - ` projections.sessions5.totp_checked_at,` + - ` projections.sessions5.otp_sms_checked_at,` + - ` projections.sessions5.otp_email_checked_at,` + - ` projections.sessions5.metadata,` + - ` projections.sessions5.token_id` + - ` FROM projections.sessions5` + - ` LEFT JOIN projections.login_names2 ON projections.sessions5.user_id = projections.login_names2.user_id AND projections.sessions5.instance_id = projections.login_names2.instance_id` + - ` LEFT JOIN projections.users8_humans ON projections.sessions5.user_id = projections.users8_humans.user_id AND projections.sessions5.instance_id = projections.users8_humans.instance_id` + - ` LEFT JOIN projections.users8 ON projections.sessions5.user_id = projections.users8.id AND projections.sessions5.instance_id = projections.users8.instance_id` + + ` projections.sessions6.password_checked_at,` + + ` projections.sessions6.intent_checked_at,` + + ` projections.sessions6.webauthn_checked_at,` + + ` projections.sessions6.webauthn_user_verified,` + + ` projections.sessions6.totp_checked_at,` + + ` projections.sessions6.otp_sms_checked_at,` + + ` projections.sessions6.otp_email_checked_at,` + + ` projections.sessions6.metadata,` + + ` projections.sessions6.token_id,` + + ` projections.sessions6.user_agent_fingerprint_id,` + + ` projections.sessions6.user_agent_ip,` + + ` projections.sessions6.user_agent_description,` + + ` projections.sessions6.user_agent_header` + + ` FROM projections.sessions6` + + ` LEFT JOIN projections.login_names2 ON projections.sessions6.user_id = projections.login_names2.user_id AND projections.sessions6.instance_id = projections.login_names2.instance_id` + + ` LEFT JOIN projections.users8_humans ON projections.sessions6.user_id = projections.users8_humans.user_id AND projections.sessions6.instance_id = projections.users8_humans.instance_id` + + ` LEFT JOIN projections.users8 ON projections.sessions6.user_id = projections.users8.id AND projections.sessions6.instance_id = projections.users8.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions5.id,` + - ` projections.sessions5.creation_date,` + - ` projections.sessions5.change_date,` + - ` projections.sessions5.sequence,` + - ` projections.sessions5.state,` + - ` projections.sessions5.resource_owner,` + - ` projections.sessions5.creator,` + - ` projections.sessions5.user_id,` + - ` projections.sessions5.user_checked_at,` + + expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions6.id,` + + ` projections.sessions6.creation_date,` + + ` projections.sessions6.change_date,` + + ` projections.sessions6.sequence,` + + ` projections.sessions6.state,` + + ` projections.sessions6.resource_owner,` + + ` projections.sessions6.creator,` + + ` projections.sessions6.user_id,` + + ` projections.sessions6.user_checked_at,` + ` projections.login_names2.login_name,` + ` projections.users8_humans.display_name,` + ` projections.users8.resource_owner,` + - ` projections.sessions5.password_checked_at,` + - ` projections.sessions5.intent_checked_at,` + - ` projections.sessions5.webauthn_checked_at,` + - ` projections.sessions5.webauthn_user_verified,` + - ` projections.sessions5.totp_checked_at,` + - ` projections.sessions5.otp_sms_checked_at,` + - ` projections.sessions5.otp_email_checked_at,` + - ` projections.sessions5.metadata,` + + ` projections.sessions6.password_checked_at,` + + ` projections.sessions6.intent_checked_at,` + + ` projections.sessions6.webauthn_checked_at,` + + ` projections.sessions6.webauthn_user_verified,` + + ` projections.sessions6.totp_checked_at,` + + ` projections.sessions6.otp_sms_checked_at,` + + ` projections.sessions6.otp_email_checked_at,` + + ` projections.sessions6.metadata,` + ` COUNT(*) OVER ()` + - ` FROM projections.sessions5` + - ` LEFT JOIN projections.login_names2 ON projections.sessions5.user_id = projections.login_names2.user_id AND projections.sessions5.instance_id = projections.login_names2.instance_id` + - ` LEFT JOIN projections.users8_humans ON projections.sessions5.user_id = projections.users8_humans.user_id AND projections.sessions5.instance_id = projections.users8_humans.instance_id` + - ` LEFT JOIN projections.users8 ON projections.sessions5.user_id = projections.users8.id AND projections.sessions5.instance_id = projections.users8.instance_id` + + ` FROM projections.sessions6` + + ` LEFT JOIN projections.login_names2 ON projections.sessions6.user_id = projections.login_names2.user_id AND projections.sessions6.instance_id = projections.login_names2.instance_id` + + ` LEFT JOIN projections.users8_humans ON projections.sessions6.user_id = projections.users8_humans.user_id AND projections.sessions6.instance_id = projections.users8_humans.instance_id` + + ` LEFT JOIN projections.users8 ON projections.sessions6.user_id = projections.users8.id AND projections.sessions6.instance_id = projections.users8.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) sessionCols = []string{ @@ -92,6 +99,10 @@ var ( "otp_email_checked_at", "metadata", "token", + "user_agent_fingerprint_id", + "user_agent_ip", + "user_agent_description", + "user_agent_header", } sessionsCols = []string{ @@ -443,6 +454,10 @@ func Test_SessionPrepare(t *testing.T) { testNow, []byte(`{"key": "dmFsdWU="}`), "tokenID", + "fingerPrintID", + "1.2.3.4", + "agentDescription", + []byte(`{"foo":["foo","bar"]}`), }, ), }, @@ -483,6 +498,12 @@ func Test_SessionPrepare(t *testing.T) { Metadata: map[string][]byte{ "key": []byte("value"), }, + UserAgent: domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + IP: net.IPv4(1, 2, 3, 4), + Description: gu.Ptr("agentDescription"), + Header: http.Header{"foo": []string{"foo", "bar"}}, + }, }, }, { diff --git a/internal/repository/session/session.go b/internal/repository/session/session.go index d7b8bd76b9..83d905ea8c 100644 --- a/internal/repository/session/session.go +++ b/internal/repository/session/session.go @@ -33,6 +33,7 @@ const ( type AddedEvent struct { eventstore.BaseEvent `json:"-"` + UserAgent *domain.UserAgent `json:"user_agent,omitempty"` } func (e *AddedEvent) Payload() interface{} { @@ -45,6 +46,7 @@ func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { func NewAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, + userAgent *domain.UserAgent, ) *AddedEvent { return &AddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -52,6 +54,7 @@ func NewAddedEvent(ctx context.Context, aggregate, AddedType, ), + UserAgent: userAgent, } } diff --git a/pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go b/pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go new file mode 100644 index 0000000000..fe2509bd95 --- /dev/null +++ b/pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go @@ -0,0 +1,12 @@ +package user + +import "github.com/zitadel/zitadel/internal/api/grpc/server/middleware" + +// OrganisationFromRequest implements deprecated [middleware.OrganisationFromRequest] interface. +// it will be removed before going GA (https://github.com/zitadel/zitadel/issues/6718) +func (r *AddHumanUserRequest) OrganisationFromRequest() *middleware.Organization { + return &middleware.Organization{ + ID: r.GetOrganisation().GetOrgId(), + Domain: r.GetOrganisation().GetOrgDomain(), + } +} diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 620936f12b..c1f32616b2 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -3763,7 +3763,7 @@ service AdminService { // Activates the "LoginDefaultOrg" feature by setting the flag to "true" // This is irreversible! - // Once activated, the login UI will use the settings of the default org (and not from the instance) if not organisation context is set + // Once activated, the login UI will use the settings of the default org (and not from the instance) if not organization context is set rpc ActivateFeatureLoginDefaultOrg(ActivateFeatureLoginDefaultOrgRequest) returns (ActivateFeatureLoginDefaultOrgResponse) { option (google.api.http) = { put: "/features/login_default_org" @@ -4887,7 +4887,7 @@ message AddGenericOAuthProviderRequest { } ]; string client_secret = 3 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret generated by the identity provider"; @@ -4954,7 +4954,7 @@ message UpdateGenericOAuthProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 4 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret will only be updated if provided"; @@ -5025,7 +5025,7 @@ message AddGenericOIDCProviderRequest { } ]; string client_secret = 4 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "secret generated by the identity provider" @@ -5076,7 +5076,7 @@ message UpdateGenericOIDCProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 5 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "client secret will only be updated if provided"; @@ -6837,7 +6837,7 @@ message SetDefaultVerifyEmailMessageTextRequest { string text = 6 [ (validate.rules).string = {max_bytes: 40000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" + example: "\"A new email has been added. Please use the button below to verify your email. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" max_length: 10000; } ]; diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 6770f77427..3c485f7a92 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -1255,7 +1255,7 @@ service ManagementService { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { summary: "Remove Multi-Factor OTP"; - description: "Remove the configured One-Time-Password (OTP) as a factor from the user. OTP is an authentication app, like Authy or Google/Microsoft Authenticator.." + description: "Remove the configured One-Time-Password (OTP) as a factor from the user. OTP is an authentication app, like Authy or Google/Microsoft Authenticator." tags: "Users"; tags: "User Human"; responses: { @@ -1306,6 +1306,68 @@ service ManagementService { }; } + rpc RemoveHumanAuthFactorOTPSMS(RemoveHumanAuthFactorOTPSMSRequest) returns (RemoveHumanAuthFactorOTPSMSResponse) { + option (google.api.http) = { + delete: "/users/{user_id}/auth_factors/otp_sms" + }; + + option (zitadel.v1.auth_option) = { + permission: "user.write" + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Remove Multi-Factor OTP SMS"; + description: "Remove the configured One-Time-Password (OTP) SMS as a factor from the user. As only one OTP SMS per user is allowed, the user will not have OTP SMS as a second-factor afterward." + tags: "Users"; + tags: "User Human"; + responses: { + key: "200" + value: { + description: "OK"; + } + }; + parameters: { + headers: { + name: "x-zitadel-orgid"; + description: "The default is always the organization of the requesting user. If you like to get a user from another organization include the header. Make sure the requesting user has permission in the requested organization."; + type: STRING, + required: false; + }; + }; + }; + } + + rpc RemoveHumanAuthFactorOTPEmail(RemoveHumanAuthFactorOTPEmailRequest) returns (RemoveHumanAuthFactorOTPEmailResponse) { + option (google.api.http) = { + delete: "/users/{user_id}/auth_factors/otp_email" + }; + + option (zitadel.v1.auth_option) = { + permission: "user.write" + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Remove Multi-Factor OTP SMS"; + description: "Remove the configured One-Time-Password (OTP) Email as a factor from the user. As only one OTP Email per user is allowed, the user will not have OTP Email as a second-factor afterward." + tags: "Users"; + tags: "User Human"; + responses: { + key: "200" + value: { + description: "OK"; + } + }; + parameters: { + headers: { + name: "x-zitadel-orgid"; + description: "The default is always the organization of the requesting user. If you like to get a user from another organization include the header. Make sure the requesting user has permission in the requested organization."; + type: STRING, + required: false; + }; + }; + }; + } + rpc ListHumanPasswordless(ListHumanPasswordlessRequest) returns (ListHumanPasswordlessResponse) { option (google.api.http) = { post: "/users/{user_id}/passwordless/_search" @@ -6806,7 +6868,7 @@ service ManagementService { }; } - // Add a new Azure AD identity provider in the organisation + // Add a new Azure AD identity provider in the organization rpc AddAzureADProvider(AddAzureADProviderRequest) returns (AddAzureADProviderResponse) { option (google.api.http) = { post: "/idps/azure" @@ -6824,7 +6886,7 @@ service ManagementService { }; } - // Change an existing Azure AD identity provider in the organisation + // Change an existing Azure AD identity provider in the organization rpc UpdateAzureADProvider(UpdateAzureADProviderRequest) returns (UpdateAzureADProviderResponse) { option (google.api.http) = { put: "/idps/azure/{id}" @@ -8246,6 +8308,22 @@ message RemoveHumanAuthFactorU2FResponse { zitadel.v1.ObjectDetails details = 1; } +message RemoveHumanAuthFactorOTPSMSRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message RemoveHumanAuthFactorOTPSMSResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message RemoveHumanAuthFactorOTPEmailRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message RemoveHumanAuthFactorOTPEmailResponse { + zitadel.v1.ObjectDetails details = 1; +} + message ListHumanPasswordlessRequest { string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; } @@ -10914,7 +10992,7 @@ message SetCustomVerifyEmailMessageTextRequest { string text = 6 [ (validate.rules).string = {max_bytes: 40000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" + example: "\"A new email has been added. Please use the button below to verify your email. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" max_length: 10000; } ]; @@ -11759,7 +11837,7 @@ message AddGenericOAuthProviderRequest { } ]; string client_secret = 3 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret generated by the identity provider"; @@ -11826,7 +11904,7 @@ message UpdateGenericOAuthProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 4 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret will only be updated if provided"; @@ -11897,7 +11975,7 @@ message AddGenericOIDCProviderRequest { } ]; string client_secret = 4 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "secret generated by the identity provider" @@ -11948,7 +12026,7 @@ message UpdateGenericOIDCProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 5 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "client secret will only be updated if provided"; diff --git a/proto/zitadel/object/v2beta/object.proto b/proto/zitadel/object/v2beta/object.proto index 75f82d98ae..c7c68f31ce 100644 --- a/proto/zitadel/object/v2beta/object.proto +++ b/proto/zitadel/object/v2beta/object.proto @@ -8,6 +8,7 @@ import "google/protobuf/timestamp.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; import "validate/validate.proto"; +// Deprecated: use Organization message Organisation { oneof org { string org_id = 1; @@ -15,6 +16,13 @@ message Organisation { } } +message Organization { + oneof org { + string org_id = 1; + string org_domain = 2; + } +} + message RequestContext { oneof resource_owner { string org_id = 1; diff --git a/proto/zitadel/project.proto b/proto/zitadel/project.proto index e050ba3dfa..b487a2dd34 100644 --- a/proto/zitadel/project.proto +++ b/proto/zitadel/project.proto @@ -48,7 +48,7 @@ message GrantedProject { ]; string granted_org_name = 3 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"Some Organisation\"" + example: "\"Some Organization\"" } ]; repeated string granted_role_keys = 4 [ diff --git a/proto/zitadel/session/v2beta/session.proto b/proto/zitadel/session/v2beta/session.proto index ddbe143361..08ef97e215 100644 --- a/proto/zitadel/session/v2beta/session.proto +++ b/proto/zitadel/session/v2beta/session.proto @@ -39,6 +39,7 @@ message Session { description: "\"custom key value list\""; } ]; + UserAgent user_agent = 7; } message Factors { @@ -72,9 +73,15 @@ message UserFactor { description: "\"display name of the checked user\""; } ]; + // deprecated: use organization_id, will be remove before GA (https://github.com/zitadel/zitadel/issues/6718) string organisation_id = 5 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "\"organisation id of the checked user\""; + description: "\"organization id of the checked user; deprecated: use organization_id\""; + } + ]; + string organization_id = 6 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "\"organization id of the checked user\""; } ]; } @@ -131,3 +138,18 @@ message SearchQuery { message IDsQuery { repeated string ids = 1; } + +message UserAgent { + optional string fingerprint_id = 1; + optional string ip = 2; + optional string description = 3; + + // A header may have multiple values. + // In Go, headers are defined + // as map[string][]string, but protobuf + // doesn't allow this scheme. + message HeaderValues { + repeated string values = 1; + } + map header = 4; +} \ No newline at end of file diff --git a/proto/zitadel/session/v2beta/session_service.proto b/proto/zitadel/session/v2beta/session_service.proto index 745c430019..718a29381f 100644 --- a/proto/zitadel/session/v2beta/session_service.proto +++ b/proto/zitadel/session/v2beta/session_service.proto @@ -274,6 +274,7 @@ message CreateSessionRequest{ } ]; RequestChallenges challenges = 3; + UserAgent user_agent = 4; } message CreateSessionResponse{ diff --git a/proto/zitadel/user/v2beta/user_service.proto b/proto/zitadel/user/v2beta/user_service.proto index 01abeceed4..36517dc4c5 100644 --- a/proto/zitadel/user/v2beta/user_service.proto +++ b/proto/zitadel/user/v2beta/user_service.proto @@ -119,7 +119,7 @@ service UserService { option (zitadel.protoc_gen_zitadel.v2.options) = { auth_option: { permission: "user.write" - org_field: "organisation" + org_field: "organization" } http_response: { success_code: 201 @@ -655,7 +655,13 @@ message AddHumanUserRequest{ example: "\"minnie-mouse\""; } ]; - zitadel.object.v2beta.Organisation organisation = 3; + // deprecated: use organization (if both are set, organization will take precedence) + zitadel.object.v2beta.Organisation organisation = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "deprecated: use organization (if both are set, organization will take precedence)" + } + ]; + zitadel.object.v2beta.Organization organization = 11; SetHumanProfile profile = 4 [ (validate.rules).message.required = true, (google.api.field_behavior) = REQUIRED