From bd23a7a56f3267af59365f7b4586a7d549da6a5f Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Thu, 19 Oct 2023 12:34:00 +0200 Subject: [PATCH] merge main into next --- .github/workflows/console.yml | 1 + .github/workflows/core-integration-test.yml | 1 + .github/workflows/core-unit-test.yml | 1 + .github/workflows/core.yml | 1 + Makefile | 2 +- build/workflow.Dockerfile | 4 +- cmd/defaults.yaml | 2 +- cmd/setup/steps.yaml | 4 +- cmd/start/start.go | 2 +- .../auth-user-mfa/auth-user-mfa.component.ts | 4 +- .../user-mfa/user-mfa.component.ts | 30 ++++ console/src/app/services/mgmt.service.ts | 16 ++ docs/docs/apis/actions/modules.md | 55 ++++++ docs/docs/legal/onboarding-support.md | 87 ++++++++++ docs/docs/legal/support-services.md | 5 +- docs/docs/support/trainings/application.md | 36 ---- docs/docs/support/trainings/introduction.md | 30 ---- docs/docs/support/trainings/project.md | 35 ---- docs/docs/support/trainings/recurring.md | 33 ---- docs/sidebars.js | 17 +- go.mod | 30 ++-- go.sum | 53 +++--- internal/actions/uuid_module.go | 83 +++++++++ internal/api/authz/token.go | 6 +- internal/api/grpc/management/information.go | 2 +- internal/api/grpc/management/user.go | 22 ++- internal/api/grpc/oidc/v2/oidc.go | 2 +- .../api/grpc/oidc/v2/oidc_integration_test.go | 12 +- internal/api/grpc/oidc/v2/server.go | 2 +- .../server/middleware/auth_interceptor.go | 40 ++++- internal/api/grpc/session/v2/session.go | 57 ++++++- .../session/v2/session_integration_test.go | 84 ++++++--- internal/api/grpc/session/v2/session_test.go | 161 +++++++++++++++++- .../api/grpc/system/instance_converter.go | 2 +- internal/api/oidc/auth_request.go | 4 +- internal/api/oidc/auth_request_converter.go | 4 +- .../api/oidc/auth_request_converter_v2.go | 2 +- .../api/oidc/auth_request_integration_test.go | 91 +++++----- internal/api/oidc/client.go | 10 +- internal/api/oidc/client_converter.go | 4 +- internal/api/oidc/client_credentials.go | 4 +- internal/api/oidc/client_integration_test.go | 14 +- internal/api/oidc/device_auth.go | 4 +- internal/api/oidc/jwt-profile.go | 4 +- internal/api/oidc/key.go | 4 +- internal/api/oidc/oidc_integration_test.go | 12 +- internal/api/oidc/op.go | 4 +- internal/api/saml/certificate.go | 2 +- internal/api/ui/console/console.go | 2 +- internal/api/ui/login/custom_action.go | 10 +- .../api/ui/login/external_provider_handler.go | 4 +- internal/api/ui/login/jwt_handler.go | 2 +- .../eventstore/token_verifier.go | 8 +- internal/command/auth_request_test.go | 44 ++++- internal/command/idp_intent.go | 2 +- internal/command/idp_intent_test.go | 2 +- internal/command/idp_model.go | 2 +- internal/command/instance_idp_test.go | 2 +- internal/command/oidc_session_test.go | 22 ++- internal/command/org_idp_test.go | 2 +- internal/command/org_test.go | 2 +- internal/command/session.go | 8 +- internal/command/session_test.go | 88 ++++++++-- .../command/user_human_refresh_token_test.go | 2 +- internal/crypto/passwap.go | 2 +- internal/crypto/passwap_test.go | 5 +- internal/domain/user_agent.go | 17 ++ internal/idp/providers/apple/apple.go | 6 +- internal/idp/providers/apple/session.go | 2 +- internal/idp/providers/apple/session_test.go | 2 +- internal/idp/providers/azuread/azuread.go | 2 +- .../idp/providers/azuread/azuread_test.go | 4 +- internal/idp/providers/azuread/session.go | 4 +- .../idp/providers/azuread/session_test.go | 2 +- internal/idp/providers/github/session_test.go | 2 +- internal/idp/providers/gitlab/gitlab.go | 2 +- internal/idp/providers/gitlab/session_test.go | 4 +- internal/idp/providers/google/google.go | 2 +- internal/idp/providers/google/session_test.go | 4 +- internal/idp/providers/jwt/session.go | 4 +- internal/idp/providers/jwt/session_test.go | 4 +- internal/idp/providers/oauth/oauth2.go | 4 +- internal/idp/providers/oauth/oauth2_test.go | 2 +- internal/idp/providers/oauth/session.go | 6 +- internal/idp/providers/oauth/session_test.go | 2 +- internal/idp/providers/oidc/oidc.go | 6 +- internal/idp/providers/oidc/oidc_test.go | 4 +- internal/idp/providers/oidc/session.go | 6 +- internal/idp/providers/oidc/session_test.go | 6 +- internal/integration/client.go | 2 +- internal/integration/integration.go | 4 +- internal/integration/oidc.go | 24 +-- .../protoc-gen-zitadel/zitadel.pb.go.tmpl | 4 +- internal/query/prepare_test.go | 12 +- internal/query/projection/session.go | 94 ++++++---- internal/query/projection/session_test.go | 38 +++-- internal/query/session.go | 33 ++++ internal/query/sessions_test.go | 107 +++++++----- internal/repository/session/session.go | 3 + .../v2beta/user_service_org.pb.zitadel.go | 12 ++ proto/zitadel/admin.proto | 12 +- proto/zitadel/management.proto | 94 +++++++++- proto/zitadel/object/v2beta/object.proto | 8 + proto/zitadel/project.proto | 2 +- proto/zitadel/session/v2beta/session.proto | 24 ++- .../session/v2beta/session_service.proto | 1 + proto/zitadel/user/v2beta/user_service.proto | 10 +- 107 files changed, 1321 insertions(+), 554 deletions(-) create mode 100644 docs/docs/legal/onboarding-support.md delete mode 100644 docs/docs/support/trainings/application.md delete mode 100644 docs/docs/support/trainings/introduction.md delete mode 100644 docs/docs/support/trainings/project.md delete mode 100644 docs/docs/support/trainings/recurring.md create mode 100644 internal/actions/uuid_module.go create mode 100644 internal/domain/user_agent.go create mode 100644 pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go diff --git a/.github/workflows/console.yml b/.github/workflows/console.yml index bc4c5f089..8471bf5af 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 c99987d25..f57a79767 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 c831758d4..3097cfd57 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 c5d868533..45c4bbda0 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 6495993ed..dd07fe49c 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 c9fb7e6c2..db27daf91 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 7897e1283..a681bedca 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 1497b7be4..c585c535b 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 dfc759e3a..744e1f80a 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 6a81838c4..c4a3d6403 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 c9be008e0..29418e80f 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 c80e9e9b4..f2389a25e 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 2cc99a222..6624ff9de 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 000000000..e9c76ac38 --- /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 1c0f36b3f..1a6f0a155 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 cf313bb96..000000000 --- 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 c597470f3..000000000 --- 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 35f3259b4..000000000 --- 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 29982cf62..000000000 --- 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 1c82d8285..a2ba179c8 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 f4e513fbb..875f6bb0e 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 9e7ae585c..018d31f9e 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 000000000..15d299212 --- /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 afb4af4bf..e5b34af9a 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 29ce6ac01..d18e115bf 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 d7a9c56dc..c61832169 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 d35a5dcff..465303b98 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 b64b71325..db11f9022 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 a7587dfdc..823594fbd 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 96426e857..d2a81203e 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 fe8e0d874..f98983936 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 1dcad7bc5..9aba59ee3 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 33804caba..8422b675b 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 4eea0a18f..551079aec 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 4ba077d04..56c0902b1 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 5999f21e5..8fbd18530 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 9f4c0d0a1..3a35b0157 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 532814bee..5b013fe86 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 6a8dc6be6..7c5628817 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 5f3f25f75..ec208db27 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 fda1f6c94..3c2f272ea 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 11121a8fa..8a7f58a44 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 7eee06096..80298e8af 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 47805783c..e2592a573 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 82884101d..3a2a6ae32 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 1a1516040..7738e3d61 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 da96302e6..c165b117b 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 d2bf3bf3e..6dd02e1bc 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 7e00605b7..6f632136f 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 516f7bc3d..f7287dd1d 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 c9cc94203..d9c8c04fc 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 51b079584..aa7a4466d 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 d67b2dcc7..42a9ba350 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 0c1bb1cf3..183e1b57c 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 265293a3a..edf28ef46 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 4f5b35c33..400b0faf0 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 b4d932fb2..e9949fb40 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 3e45d8266..8989f4c92 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 ab850aeea..a517814e0 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 d6eef4a39..8f966e740 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 6db8654d9..6f00765bc 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 a58c5d2d3..caf3056f7 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 e478792e1..7e443a4c5 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 6ad7f6865..c623c737b 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 a5a293a44..479d5731e 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 b557ca4a5..0538ac631 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 000000000..ca72c6bec --- /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 9463cb61b..9664e006c 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 ae629f957..5e9143f05 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 207f21981..3c1ab5976 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 244f383e1..46445a397 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 3febb43f9..122a70bb0 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 5bc7bb84c..698cdad19 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 531d909b9..f68c4cc7d 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 2ad9f449f..247ef35c6 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 1bb02302f..76e9a74b4 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 de5904432..0853f3ce3 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 7036d4e10..221f2b61a 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 d3edde0ea..1915adf1b 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 be3ffc053..54fcc039e 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 ae79ed22e..3a8210aec 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 a31e9d4c2..ad8cbbd45 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 5fdfbc218..814a7ac9c 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 eb9ab49ef..065ad3b21 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 9901ea11c..ce6fa3f10 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 aab525548..30d4d11ab 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 bbe08155c..d510bf15c 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 366e42643..bb44fb114 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 afaa35804..200dac8fc 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 71bc9805c..98a577be1 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 3e270b1d7..61d7f9d90 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 ad1a44de3..b6edcd3ae 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 bc0123c75..adb71c42f 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 242b38740..c9770c0dd 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 816969d66..5bfc5dd8c 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 607b028ff..b27b44399 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 bcc87a303..c2921d235 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 fa5209bdd..1bae095ec 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 d7b8bd76b..83d905ea8 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 000000000..fe2509bd9 --- /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 620936f12..c1f32616b 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 6770f7742..3c485f7a9 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 75f82d98a..c7c68f31c 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 e050ba3df..b487a2dd3 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 ddbe14336..08ef97e21 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 745c43001..718a29381 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 01abeceed..36517dc4c 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