mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 19:47:34 +00:00
Merge branch 'main' into qa
This commit is contained in:
42
.github/workflows/docker.yml
vendored
42
.github/workflows/docker.yml
vendored
@@ -4,6 +4,10 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -39,7 +43,14 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
driver-opts: 'image=moby/buildkit:v0.11.6'
|
driver-opts: 'image=moby/buildkit:v0.11.6'
|
||||||
|
|
||||||
- name: Login
|
- name: Login Public
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login Private
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ secrets.DOCKER_REGISTRY }}
|
registry: ${{ secrets.DOCKER_REGISTRY }}
|
||||||
@@ -50,9 +61,15 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ secrets.DOCKER_IMAGE }}
|
images: |
|
||||||
# generate Docker tags based on the following events/attributes
|
ghcr.io/zitadel/login
|
||||||
tags: type=sha
|
${{ secrets.DOCKER_IMAGE }}
|
||||||
|
tags: |
|
||||||
|
type=edge
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag
|
||||||
|
type=ref,event=pr
|
||||||
|
type=sha
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
@@ -69,8 +86,23 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
push: true
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/digests/app
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "/tmp/digests/app/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: digests
|
||||||
|
path: /tmp/digests
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@@ -2,9 +2,9 @@ name: Quality
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
schedule:
|
# schedule:
|
||||||
# Every morning at 6:00 AM CET
|
# Every morning at 6:00 AM CET
|
||||||
- cron: '0 4 * * *'
|
# - cron: '0 4 * * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
target-env:
|
target-env:
|
||||||
@@ -110,6 +110,14 @@ jobs:
|
|||||||
run: pnpm build
|
run: pnpm build
|
||||||
if: ${{ startsWith(matrix.command, 'test:acceptance') }}
|
if: ${{ startsWith(matrix.command, 'test:acceptance') }}
|
||||||
|
|
||||||
|
- name: Run SAML SP
|
||||||
|
run: ZITADEL_DEV_UID=root pnpm run-samlsp
|
||||||
|
if: ${{ matrix.command == 'test:acceptance' }}
|
||||||
|
|
||||||
|
- name: Run OIDC RP
|
||||||
|
run: ZITADEL_DEV_UID=root pnpm run-oidcrp
|
||||||
|
if: ${{ matrix.command == 'test:acceptance' }}
|
||||||
|
|
||||||
- name: Check
|
- name: Check
|
||||||
id: check
|
id: check
|
||||||
run: pnpm ${{ contains(matrix.command, 'test:acceptance') && 'test:acceptance' || matrix.command }}
|
run: pnpm ${{ contains(matrix.command, 'test:acceptance') && 'test:acceptance' || matrix.command }}
|
||||||
|
@@ -47,11 +47,8 @@ export ZITADEL_DEV_UID="$(id -u)"
|
|||||||
# Pull images
|
# Pull images
|
||||||
docker compose --file ./acceptance/docker-compose.yaml pull
|
docker compose --file ./acceptance/docker-compose.yaml pull
|
||||||
|
|
||||||
# Run ZITADEL and configure ./apps/login/.env.local
|
# Run ZITADEL with local notification sink and configure ./apps/login/.env.local
|
||||||
docker compose --file ./acceptance/docker-compose.yaml run setup
|
pnpm run-sink
|
||||||
|
|
||||||
# Configure your shell to use the environment variables written to ./apps/login/.env.acceptance
|
|
||||||
export $(cat ./apps/login/.env.acceptance | xargs)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Developing Against Your ZITADEL Cloud Instance
|
### Developing Against Your ZITADEL Cloud Instance
|
||||||
@@ -79,6 +76,22 @@ pnpm dev
|
|||||||
|
|
||||||
The application is now available at `http://localhost:3000`
|
The application is now available at `http://localhost:3000`
|
||||||
|
|
||||||
|
### Adding applications and IDPs
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# OPTIONAL Run SAML SP
|
||||||
|
pnpm run-samlsp
|
||||||
|
|
||||||
|
# OPTIONAL Run OIDC RP
|
||||||
|
pnpm run-oidcrp
|
||||||
|
|
||||||
|
# OPTIONAL Run SAML IDP
|
||||||
|
pnpm run-samlidp
|
||||||
|
|
||||||
|
# OPTIONAL Run OIDC OP
|
||||||
|
pnpm run-oidcop
|
||||||
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
You can execute the following commands `pnpm test` for a single test run or `pnpm test:watch` in the following directories:
|
You can execute the following commands `pnpm test` for a single test run or `pnpm test:watch` in the following directories:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.19-alpine
|
FROM golang:1.24-alpine
|
||||||
RUN apk add curl jq
|
RUN apk add curl jq
|
||||||
COPY setup.sh /setup.sh
|
COPY setup.sh /setup.sh
|
||||||
RUN chmod +x /setup.sh
|
RUN chmod +x /setup.sh
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
zitadel:
|
zitadel:
|
||||||
user: "${ZITADEL_DEV_UID}"
|
user: "${ZITADEL_DEV_UID}"
|
||||||
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:v2.67.2}"
|
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:02617cf17fdde849378c1a6b5254bbfb2745b164}"
|
||||||
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled --config /zitadel.yaml --steps /zitadel.yaml'
|
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled --config /zitadel.yaml --steps /zitadel.yaml'
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
@@ -11,6 +11,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: "service_healthy"
|
condition: "service_healthy"
|
||||||
|
extra_hosts:
|
||||||
|
- "localhost:host-gateway"
|
||||||
|
|
||||||
db:
|
db:
|
||||||
restart: "always"
|
restart: "always"
|
||||||
@@ -57,7 +59,7 @@ services:
|
|||||||
condition: "service_completed_successfully"
|
condition: "service_completed_successfully"
|
||||||
|
|
||||||
sink:
|
sink:
|
||||||
image: golang:1.19-alpine
|
image: golang:1.24-alpine
|
||||||
container_name: sink
|
container_name: sink
|
||||||
command: go run /sink/main.go -port '3333' -email '/email' -sms '/sms' -notification '/notification'
|
command: go run /sink/main.go -port '3333' -email '/email' -sms '/sms' -notification '/notification'
|
||||||
ports:
|
ports:
|
||||||
|
20
acceptance/idp/oidc/docker-compose.yaml
Normal file
20
acceptance/idp/oidc/docker-compose.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
oidcop:
|
||||||
|
image: golang:1.24-alpine
|
||||||
|
container_name: oidcop
|
||||||
|
command: go run main.go
|
||||||
|
environment:
|
||||||
|
API_URL: 'http://localhost:8080'
|
||||||
|
API_DOMAIN: 'localhost:8080'
|
||||||
|
PAT_FILE: '/pat/zitadel-admin-sa.pat'
|
||||||
|
SCHEMA: 'http'
|
||||||
|
HOST: 'localhost'
|
||||||
|
PORT: "8004"
|
||||||
|
working_dir: /oidc
|
||||||
|
ports:
|
||||||
|
- 8004:8004
|
||||||
|
volumes:
|
||||||
|
- "../../pat:/pat"
|
||||||
|
- "./:/oidc"
|
||||||
|
extra_hosts:
|
||||||
|
- "localhost:host-gateway"
|
28
acceptance/idp/oidc/go.mod
Normal file
28
acceptance/idp/oidc/go.mod
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module github.com/zitadel/typescript/acceptance/idp/oidc
|
||||||
|
|
||||||
|
go 1.24.1
|
||||||
|
|
||||||
|
require github.com/zitadel/oidc/v3 v3.37.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
|
||||||
|
github.com/go-chi/chi/v5 v5.2.1 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
github.com/muhlemmer/gu v0.3.1 // indirect
|
||||||
|
github.com/muhlemmer/httpforwarded v0.1.0 // indirect
|
||||||
|
github.com/rs/cors v1.11.1 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/zitadel/logging v0.6.2 // indirect
|
||||||
|
github.com/zitadel/schema v1.3.1 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||||
|
golang.org/x/crypto v0.35.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.28.0 // indirect
|
||||||
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
|
)
|
71
acceptance/idp/oidc/go.sum
Normal file
71
acceptance/idp/oidc/go.sum
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM=
|
||||||
|
github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM=
|
||||||
|
github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY=
|
||||||
|
github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||||
|
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU=
|
||||||
|
github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4=
|
||||||
|
github.com/zitadel/oidc/v3 v3.37.0 h1:nYATWlnP7f18XiAbw6upUruBaqfB1kUrXrSTf1EYGO8=
|
||||||
|
github.com/zitadel/oidc/v3 v3.37.0/go.mod h1:/xDan4OUQhguJ4Ur73OOJrtugvR164OMnidXP9xfVNw=
|
||||||
|
github.com/zitadel/schema v1.3.1 h1:QT3kwiRIRXXLVAs6gCK/u044WmUVh6IlbLXUsn6yRQU=
|
||||||
|
github.com/zitadel/schema v1.3.1/go.mod h1:071u7D2LQacy1HAN+YnMd/mx1qVE2isb0Mjeqg46xnU=
|
||||||
|
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||||
|
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
|
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||||
|
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
186
acceptance/idp/oidc/main.go
Normal file
186
acceptance/idp/oidc/main.go
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/oidc/v3/example/server/exampleop"
|
||||||
|
"github.com/zitadel/oidc/v3/example/server/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
apiURL := os.Getenv("API_URL")
|
||||||
|
pat := readPAT(os.Getenv("PAT_FILE"))
|
||||||
|
domain := os.Getenv("API_DOMAIN")
|
||||||
|
schema := os.Getenv("SCHEMA")
|
||||||
|
host := os.Getenv("HOST")
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
|
||||||
|
logger := slog.New(
|
||||||
|
slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||||
|
AddSource: true,
|
||||||
|
Level: slog.LevelDebug,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
issuer := fmt.Sprintf("%s://%s:%s/", schema, host, port)
|
||||||
|
redirectURI := fmt.Sprintf("%s/idps/callback", apiURL)
|
||||||
|
|
||||||
|
clientID := "web"
|
||||||
|
clientSecret := "secret"
|
||||||
|
storage.RegisterClients(
|
||||||
|
storage.WebClient(clientID, clientSecret, redirectURI),
|
||||||
|
)
|
||||||
|
|
||||||
|
storage := storage.NewStorage(storage.NewUserStore(issuer))
|
||||||
|
router := exampleop.SetupServer(issuer, storage, logger, false)
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: ":" + port,
|
||||||
|
Handler: router,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Fatalf("HTTP server error: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("Stopped serving new connections.")
|
||||||
|
}()
|
||||||
|
|
||||||
|
createZitadelResources(apiURL, pat, domain, issuer, clientID, clientSecret)
|
||||||
|
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-sigChan
|
||||||
|
|
||||||
|
shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer shutdownRelease()
|
||||||
|
|
||||||
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||||
|
log.Fatalf("HTTP shutdown error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPAT(path string) string {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pat, err := io.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.Trim(string(pat), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createZitadelResources(apiURL, pat, domain, issuer, clientID, clientSecret string) error {
|
||||||
|
idpID, err := CreateIDP(apiURL, pat, domain, issuer, clientID, clientSecret)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ActivateIDP(apiURL, pat, domain, idpID)
|
||||||
|
}
|
||||||
|
|
||||||
|
type createIDP struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
ClientId string `json:"clientId"`
|
||||||
|
ClientSecret string `json:"clientSecret"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
ProviderOptions providerOptions `json:"providerOptions"`
|
||||||
|
IsIdTokenMapping bool `json:"isIdTokenMapping"`
|
||||||
|
UsePkce bool `json:"usePkce"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type providerOptions struct {
|
||||||
|
IsLinkingAllowed bool `json:"isLinkingAllowed"`
|
||||||
|
IsCreationAllowed bool `json:"isCreationAllowed"`
|
||||||
|
IsAutoCreation bool `json:"isAutoCreation"`
|
||||||
|
IsAutoUpdate bool `json:"isAutoUpdate"`
|
||||||
|
AutoLinking string `json:"autoLinking"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type idp struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateIDP(apiURL, pat, domain string, issuer, clientID, clientSecret string) (string, error) {
|
||||||
|
createIDP := &createIDP{
|
||||||
|
Name: "OIDC",
|
||||||
|
Issuer: issuer,
|
||||||
|
ClientId: clientID,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
Scopes: []string{"openid", "profile", "email"},
|
||||||
|
ProviderOptions: providerOptions{
|
||||||
|
IsLinkingAllowed: true,
|
||||||
|
IsCreationAllowed: true,
|
||||||
|
IsAutoCreation: true,
|
||||||
|
IsAutoUpdate: true,
|
||||||
|
AutoLinking: "AUTO_LINKING_OPTION_USERNAME",
|
||||||
|
},
|
||||||
|
IsIdTokenMapping: false,
|
||||||
|
UsePkce: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := doRequestWithHeaders(apiURL+"/admin/v1/idps/generic_oidc", pat, domain, createIDP)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
idp := new(idp)
|
||||||
|
if err := json.Unmarshal(data, idp); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return idp.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type activateIDP struct {
|
||||||
|
IdpId string `json:"idpId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ActivateIDP(apiURL, pat, domain string, idpID string) error {
|
||||||
|
activateIDP := &activateIDP{
|
||||||
|
IdpId: idpID,
|
||||||
|
}
|
||||||
|
_, err := doRequestWithHeaders(apiURL+"/admin/v1/policies/login/idps", pat, domain, activateIDP)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRequestWithHeaders(apiURL, pat, domain string, body any) (*http.Response, error) {
|
||||||
|
data, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, apiURL, io.NopCloser(bytes.NewReader(data)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values := http.Header{}
|
||||||
|
values.Add("Authorization", "Bearer "+pat)
|
||||||
|
values.Add("x-forwarded-host", domain)
|
||||||
|
values.Add("Content-Type", "application/json")
|
||||||
|
req.Header = values
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
20
acceptance/idp/saml/docker-compose.yaml
Normal file
20
acceptance/idp/saml/docker-compose.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
samlidp:
|
||||||
|
image: golang:1.24-alpine
|
||||||
|
container_name: samlidp
|
||||||
|
command: go run main.go
|
||||||
|
environment:
|
||||||
|
API_URL: 'http://localhost:8080'
|
||||||
|
API_DOMAIN: 'localhost:8080'
|
||||||
|
PAT_FILE: '/pat/zitadel-admin-sa.pat'
|
||||||
|
SCHEMA: 'http'
|
||||||
|
HOST: 'localhost'
|
||||||
|
PORT: "8003"
|
||||||
|
working_dir: /saml
|
||||||
|
ports:
|
||||||
|
- 8003:8003
|
||||||
|
volumes:
|
||||||
|
- "../../pat:/pat"
|
||||||
|
- "./:/saml"
|
||||||
|
extra_hosts:
|
||||||
|
- "localhost:host-gateway"
|
16
acceptance/idp/saml/go.mod
Normal file
16
acceptance/idp/saml/go.mod
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module github.com/zitadel/typescript/acceptance/idp/saml
|
||||||
|
|
||||||
|
go 1.24.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/crewjam/saml v0.4.14
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.1.0
|
||||||
|
github.com/zenazn/goji v1.0.1
|
||||||
|
golang.org/x/crypto v0.36.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beevik/etree v1.1.0 // indirect
|
||||||
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
|
github.com/russellhaering/goxmldsig v1.3.0 // indirect
|
||||||
|
)
|
49
acceptance/idp/saml/go.sum
Normal file
49
acceptance/idp/saml/go.sum
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||||
|
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
|
||||||
|
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||||
|
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
|
github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM=
|
||||||
|
github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8=
|
||||||
|
github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
328
acceptance/idp/saml/main.go
Normal file
328
acceptance/idp/saml/main.go
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/crewjam/saml"
|
||||||
|
"github.com/crewjam/saml/logger"
|
||||||
|
"github.com/crewjam/saml/samlidp"
|
||||||
|
xrv "github.com/mattermost/xml-roundtrip-validator"
|
||||||
|
"github.com/zenazn/goji"
|
||||||
|
"github.com/zenazn/goji/bind"
|
||||||
|
"github.com/zenazn/goji/web"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var key = func() crypto.PrivateKey {
|
||||||
|
b, _ := pem.Decode([]byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA0OhbMuizgtbFOfwbK7aURuXhZx6VRuAs3nNibiuifwCGz6u9
|
||||||
|
yy7bOR0P+zqN0YkjxaokqFgra7rXKCdeABmoLqCC0U+cGmLNwPOOA0PaD5q5xKhQ
|
||||||
|
4Me3rt/R9C4Ca6k3/OnkxnKwnogcsmdgs2l8liT3qVHP04Oc7Uymq2v09bGb6nPu
|
||||||
|
fOrkXS9F6mSClxHG/q59AGOWsXK1xzIRV1eu8W2SNdyeFVU1JHiQe444xLoPul5t
|
||||||
|
InWasKayFsPlJfWNc8EoU8COjNhfo/GovFTHVjh9oUR/gwEFVwifIHihRE0Hazn2
|
||||||
|
EQSLaOr2LM0TsRsQroFjmwSGgI+X2bfbMTqWOQIDAQABAoIBAFWZwDTeESBdrLcT
|
||||||
|
zHZe++cJLxE4AObn2LrWANEv5AeySYsyzjRBYObIN9IzrgTb8uJ900N/zVr5VkxH
|
||||||
|
xUa5PKbOcowd2NMfBTw5EEnaNbILLm+coHdanrNzVu59I9TFpAFoPavrNt/e2hNo
|
||||||
|
NMGPSdOkFi81LLl4xoadz/WR6O/7N2famM+0u7C2uBe+TrVwHyuqboYoidJDhO8M
|
||||||
|
w4WlY9QgAUhkPyzZqrl+VfF1aDTGVf4LJgaVevfFCas8Ws6DQX5q4QdIoV6/0vXi
|
||||||
|
B1M+aTnWjHuiIzjBMWhcYW2+I5zfwNWRXaxdlrYXRukGSdnyO+DH/FhHePJgmlkj
|
||||||
|
NInADDkCgYEA6MEQFOFSCc/ELXYWgStsrtIlJUcsLdLBsy1ocyQa2lkVUw58TouW
|
||||||
|
RciE6TjW9rp31pfQUnO2l6zOUC6LT9Jvlb9PSsyW+rvjtKB5PjJI6W0hjX41wEO6
|
||||||
|
fshFELMJd9W+Ezao2AsP2hZJ8McCF8no9e00+G4xTAyxHsNI2AFTCQcCgYEA5cWZ
|
||||||
|
JwNb4t7YeEajPt9xuYNUOQpjvQn1aGOV7KcwTx5ELP/Hzi723BxHs7GSdrLkkDmi
|
||||||
|
Gpb+mfL4wxCt0fK0i8GFQsRn5eusyq9hLqP/bmjpHoXe/1uajFbE1fZQR+2LX05N
|
||||||
|
3ATlKaH2hdfCJedFa4wf43+cl6Yhp6ZA0Yet1r8CgYEAwiu1j8W9G+RRA5/8/DtO
|
||||||
|
yrUTOfsbFws4fpLGDTA0mq0whf6Soy/96C90+d9qLaC3srUpnG9eB0CpSOjbXXbv
|
||||||
|
kdxseLkexwOR3bD2FHX8r4dUM2bzznZyEaxfOaQypN8SV5ME3l60Fbr8ajqLO288
|
||||||
|
wlTmGM5Mn+YCqOg/T7wjGmcCgYBpzNfdl/VafOROVbBbhgXWtzsz3K3aYNiIjbp+
|
||||||
|
MunStIwN8GUvcn6nEbqOaoiXcX4/TtpuxfJMLw4OvAJdtxUdeSmEee2heCijV6g3
|
||||||
|
ErrOOy6EqH3rNWHvlxChuP50cFQJuYOueO6QggyCyruSOnDDuc0BM0SGq6+5g5s7
|
||||||
|
H++S/wKBgQDIkqBtFr9UEf8d6JpkxS0RXDlhSMjkXmkQeKGFzdoJcYVFIwq8jTNB
|
||||||
|
nJrVIGs3GcBkqGic+i7rTO1YPkquv4dUuiIn+vKZVoO6b54f+oPBXd4S0BnuEqFE
|
||||||
|
rdKNuCZhiaE2XD9L/O9KP1fh5bfEcKwazQ23EvpJHBMm8BGC+/YZNw==
|
||||||
|
-----END RSA PRIVATE KEY-----`))
|
||||||
|
k, _ := x509.ParsePKCS1PrivateKey(b.Bytes)
|
||||||
|
return k
|
||||||
|
}()
|
||||||
|
|
||||||
|
var cert = func() *x509.Certificate {
|
||||||
|
b, _ := pem.Decode([]byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDBzCCAe+gAwIBAgIJAPr/Mrlc8EGhMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV
|
||||||
|
BAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNTEyMjgxOTE5NDVaFw0yNTEyMjUxOTE5
|
||||||
|
NDVaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
|
||||||
|
BQADggEPADCCAQoCggEBANDoWzLos4LWxTn8Gyu2lEbl4WcelUbgLN5zYm4ron8A
|
||||||
|
hs+rvcsu2zkdD/s6jdGJI8WqJKhYK2u61ygnXgAZqC6ggtFPnBpizcDzjgND2g+a
|
||||||
|
ucSoUODHt67f0fQuAmupN/zp5MZysJ6IHLJnYLNpfJYk96lRz9ODnO1Mpqtr9PWx
|
||||||
|
m+pz7nzq5F0vRepkgpcRxv6ufQBjlrFytccyEVdXrvFtkjXcnhVVNSR4kHuOOMS6
|
||||||
|
D7pebSJ1mrCmshbD5SX1jXPBKFPAjozYX6PxqLxUx1Y4faFEf4MBBVcInyB4oURN
|
||||||
|
B2s59hEEi2jq9izNE7EbEK6BY5sEhoCPl9m32zE6ljkCAwEAAaNQME4wHQYDVR0O
|
||||||
|
BBYEFB9ZklC1Ork2zl56zg08ei7ss/+iMB8GA1UdIwQYMBaAFB9ZklC1Ork2zl56
|
||||||
|
zg08ei7ss/+iMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAVoTSQ5
|
||||||
|
pAirw8OR9FZ1bRSuTDhY9uxzl/OL7lUmsv2cMNeCB3BRZqm3mFt+cwN8GsH6f3uv
|
||||||
|
NONIhgFpTGN5LEcXQz89zJEzB+qaHqmbFpHQl/sx2B8ezNgT/882H2IH00dXESEf
|
||||||
|
y/+1gHg2pxjGnhRBN6el/gSaDiySIMKbilDrffuvxiCfbpPN0NRRiPJhd2ay9KuL
|
||||||
|
/RxQRl1gl9cHaWiouWWba1bSBb2ZPhv2rPMUsFo98ntkGCObDX6Y1SpkqmoTbrsb
|
||||||
|
GFsTG2DLxnvr4GdN1BSr0Uu/KV3adj47WkXVPeMYQti/bQmxQB8tRFhrw80qakTL
|
||||||
|
UzreO96WzlBBMtY=
|
||||||
|
-----END CERTIFICATE-----`))
|
||||||
|
c, _ := x509.ParseCertificate(b.Bytes)
|
||||||
|
return c
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Example from https://github.com/crewjam/saml/blob/main/example/idp/idp.go
|
||||||
|
func main() {
|
||||||
|
apiURL := os.Getenv("API_URL")
|
||||||
|
pat := readPAT(os.Getenv("PAT_FILE"))
|
||||||
|
domain := os.Getenv("API_DOMAIN")
|
||||||
|
schema := os.Getenv("SCHEMA")
|
||||||
|
host := os.Getenv("HOST")
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
|
||||||
|
baseURL, err := url.Parse(schema + "://" + host + ":" + port)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idpServer, err := samlidp.New(samlidp.Options{
|
||||||
|
URL: *baseURL,
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
Key: key,
|
||||||
|
Certificate: cert,
|
||||||
|
Store: &samlidp.MemoryStore{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata, err := xml.MarshalIndent(idpServer.IDP.Metadata(), "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
idpID, err := createZitadelResources(apiURL, pat, domain, metadata)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lis := bind.Socket(":" + baseURL.Port())
|
||||||
|
goji.Handle("/*", idpServer)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
goji.ServeListener(lis)
|
||||||
|
}()
|
||||||
|
|
||||||
|
addService(idpServer, apiURL+"/idps/"+idpID+"/saml/metadata")
|
||||||
|
addUsers(idpServer)
|
||||||
|
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-sigChan
|
||||||
|
|
||||||
|
if err := lis.Close(); err != nil {
|
||||||
|
log.Fatalf("HTTP shutdown error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPAT(path string) string {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pat, err := io.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.Trim(string(pat), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addService(idpServer *samlidp.Server, spURLStr string) {
|
||||||
|
metadataResp, err := http.Get(spURLStr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer metadataResp.Body.Close()
|
||||||
|
|
||||||
|
idpServer.HandlePutService(
|
||||||
|
web.C{URLParams: map[string]string{"id": spURLStr}},
|
||||||
|
httptest.NewRecorder(),
|
||||||
|
httptest.NewRequest(http.MethodPost, spURLStr, metadataResp.Body),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSPMetadata(r io.Reader) (spMetadata *saml.EntityDescriptor, err error) {
|
||||||
|
var data []byte
|
||||||
|
if data, err = io.ReadAll(r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
spMetadata = &saml.EntityDescriptor{}
|
||||||
|
if err := xrv.Validate(bytes.NewBuffer(data)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := xml.Unmarshal(data, &spMetadata); err != nil {
|
||||||
|
if err.Error() == "expected element type <EntityDescriptor> but have <EntitiesDescriptor>" {
|
||||||
|
entities := &saml.EntitiesDescriptor{}
|
||||||
|
if err := xml.Unmarshal(data, &entities); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range entities.EntityDescriptors {
|
||||||
|
if len(e.SPSSODescriptors) > 0 {
|
||||||
|
return &e, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// there were no SPSSODescriptors in the response
|
||||||
|
return nil, errors.New("metadata contained no service provider metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return spMetadata, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addUsers(idpServer *samlidp.Server) {
|
||||||
|
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("hunter2"), bcrypt.DefaultCost)
|
||||||
|
err := idpServer.Store.Put("/users/alice", samlidp.User{Name: "alice",
|
||||||
|
HashedPassword: hashedPassword,
|
||||||
|
Groups: []string{"Administrators", "Users"},
|
||||||
|
Email: "alice@example.com",
|
||||||
|
CommonName: "Alice Smith",
|
||||||
|
Surname: "Smith",
|
||||||
|
GivenName: "Alice",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = idpServer.Store.Put("/users/bob", samlidp.User{
|
||||||
|
Name: "bob",
|
||||||
|
HashedPassword: hashedPassword,
|
||||||
|
Groups: []string{"Users"},
|
||||||
|
Email: "bob@example.com",
|
||||||
|
CommonName: "Bob Smith",
|
||||||
|
Surname: "Smith",
|
||||||
|
GivenName: "Bob",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createZitadelResources(apiURL, pat, domain string, metadata []byte) (string, error) {
|
||||||
|
idpID, err := CreateIDP(apiURL, pat, domain, metadata)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return idpID, ActivateIDP(apiURL, pat, domain, idpID)
|
||||||
|
}
|
||||||
|
|
||||||
|
type createIDP struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MetadataXml string `json:"metadataXml"`
|
||||||
|
Binding string `json:"binding"`
|
||||||
|
WithSignedRequest bool `json:"withSignedRequest"`
|
||||||
|
ProviderOptions providerOptions `json:"providerOptions"`
|
||||||
|
NameIdFormat string `json:"nameIdFormat"`
|
||||||
|
}
|
||||||
|
type providerOptions struct {
|
||||||
|
IsLinkingAllowed bool `json:"isLinkingAllowed"`
|
||||||
|
IsCreationAllowed bool `json:"isCreationAllowed"`
|
||||||
|
IsAutoCreation bool `json:"isAutoCreation"`
|
||||||
|
IsAutoUpdate bool `json:"isAutoUpdate"`
|
||||||
|
AutoLinking string `json:"autoLinking"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type idp struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateIDP(apiURL, pat, domain string, idpMetadata []byte) (string, error) {
|
||||||
|
encoded := make([]byte, base64.URLEncoding.EncodedLen(len(idpMetadata)))
|
||||||
|
base64.URLEncoding.Encode(encoded, idpMetadata)
|
||||||
|
|
||||||
|
createIDP := &createIDP{
|
||||||
|
Name: "CREWJAM",
|
||||||
|
MetadataXml: string(encoded),
|
||||||
|
Binding: "SAML_BINDING_REDIRECT",
|
||||||
|
WithSignedRequest: false,
|
||||||
|
ProviderOptions: providerOptions{
|
||||||
|
IsLinkingAllowed: true,
|
||||||
|
IsCreationAllowed: true,
|
||||||
|
IsAutoCreation: true,
|
||||||
|
IsAutoUpdate: true,
|
||||||
|
AutoLinking: "AUTO_LINKING_OPTION_USERNAME",
|
||||||
|
},
|
||||||
|
NameIdFormat: "SAML_NAME_ID_FORMAT_PERSISTENT",
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := doRequestWithHeaders(apiURL+"/admin/v1/idps/saml", pat, domain, createIDP)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
idp := new(idp)
|
||||||
|
if err := json.Unmarshal(data, idp); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return idp.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type activateIDP struct {
|
||||||
|
IdpId string `json:"idpId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ActivateIDP(apiURL, pat, domain string, idpID string) error {
|
||||||
|
activateIDP := &activateIDP{
|
||||||
|
IdpId: idpID,
|
||||||
|
}
|
||||||
|
_, err := doRequestWithHeaders(apiURL+"/admin/v1/policies/login/idps", pat, domain, activateIDP)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRequestWithHeaders(apiURL, pat, domain string, body any) (*http.Response, error) {
|
||||||
|
data, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, apiURL, io.NopCloser(bytes.NewReader(data)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values := http.Header{}
|
||||||
|
values.Add("Authorization", "Bearer "+pat)
|
||||||
|
values.Add("x-forwarded-host", domain)
|
||||||
|
values.Add("Content-Type", "application/json")
|
||||||
|
req.Header = values
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
22
acceptance/oidc/docker-compose.yaml
Normal file
22
acceptance/oidc/docker-compose.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
oidcrp:
|
||||||
|
image: golang:1.24-alpine
|
||||||
|
container_name: oidcrp
|
||||||
|
command: go run main.go
|
||||||
|
environment:
|
||||||
|
API_URL: 'http://localhost:8080'
|
||||||
|
API_DOMAIN: 'localhost:8080'
|
||||||
|
PAT_FILE: '/pat/zitadel-admin-sa.pat'
|
||||||
|
LOGIN_URL: 'http://localhost:3000'
|
||||||
|
ISSUER: 'http://localhost:3000'
|
||||||
|
HOST: 'http://localhost'
|
||||||
|
PORT: '8000'
|
||||||
|
SCOPES: 'openid profile email'
|
||||||
|
working_dir: /oidc
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
volumes:
|
||||||
|
- "../pat:/pat"
|
||||||
|
- "./:/oidc"
|
||||||
|
extra_hosts:
|
||||||
|
- "localhost:host-gateway"
|
26
acceptance/oidc/go.mod
Normal file
26
acceptance/oidc/go.mod
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
module github.com/zitadel/typescript/acceptance/oidc
|
||||||
|
|
||||||
|
go 1.24.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/zitadel/logging v0.6.1
|
||||||
|
github.com/zitadel/oidc/v3 v3.36.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
github.com/muhlemmer/gu v0.3.1 // indirect
|
||||||
|
github.com/zitadel/schema v1.3.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||||
|
golang.org/x/crypto v0.32.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.28.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.22.0 // indirect
|
||||||
|
)
|
67
acceptance/oidc/go.sum
Normal file
67
acceptance/oidc/go.sum
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
|
github.com/jeremija/gosubmit v0.2.8 h1:mmSITBz9JxVtu8eqbN+zmmwX7Ij2RidQxhcwRVI4wqA=
|
||||||
|
github.com/jeremija/gosubmit v0.2.8/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI=
|
||||||
|
github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM=
|
||||||
|
github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM=
|
||||||
|
github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY=
|
||||||
|
github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||||
|
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/zitadel/logging v0.6.1 h1:Vyzk1rl9Kq9RCevcpX6ujUaTYFX43aa4LkvV1TvUk+Y=
|
||||||
|
github.com/zitadel/logging v0.6.1/go.mod h1:Y4CyAXHpl3Mig6JOszcV5Rqqsojj+3n7y2F591Mp/ow=
|
||||||
|
github.com/zitadel/oidc/v3 v3.36.1 h1:1AT1NqKKEqAwx4GmKJZ9fYkWH2WIn/VKMfQ46nBtRf0=
|
||||||
|
github.com/zitadel/oidc/v3 v3.36.1/go.mod h1:dApGZLvWZTHRuxmcbQlW5d2XVjVYR3vGOdq536igmTs=
|
||||||
|
github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0=
|
||||||
|
github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc=
|
||||||
|
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||||
|
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
|
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||||
|
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
313
acceptance/oidc/main.go
Normal file
313
acceptance/oidc/main.go
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
||||||
|
httphelper "github.com/zitadel/oidc/v3/pkg/http"
|
||||||
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
callbackPath = "/auth/callback"
|
||||||
|
key = []byte("test1234test1234")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
apiURL := os.Getenv("API_URL")
|
||||||
|
pat := readPAT(os.Getenv("PAT_FILE"))
|
||||||
|
domain := os.Getenv("API_DOMAIN")
|
||||||
|
loginURL := os.Getenv("LOGIN_URL")
|
||||||
|
issuer := os.Getenv("ISSUER")
|
||||||
|
host := os.Getenv("HOST")
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
scopeList := strings.Split(os.Getenv("SCOPES"), " ")
|
||||||
|
|
||||||
|
redirectURI := fmt.Sprintf("%v:%v%v", host, port, callbackPath)
|
||||||
|
cookieHandler := httphelper.NewCookieHandler(key, key, httphelper.WithUnsecure())
|
||||||
|
|
||||||
|
clientID, clientSecret, err := createZitadelResources(apiURL, pat, domain, redirectURI, loginURL)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := slog.New(
|
||||||
|
slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||||
|
AddSource: true,
|
||||||
|
Level: slog.LevelDebug,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: time.Minute,
|
||||||
|
}
|
||||||
|
// enable outgoing request logging
|
||||||
|
logging.EnableHTTPClient(client,
|
||||||
|
logging.WithClientGroup("client"),
|
||||||
|
)
|
||||||
|
|
||||||
|
options := []rp.Option{
|
||||||
|
rp.WithCookieHandler(cookieHandler),
|
||||||
|
rp.WithVerifierOpts(rp.WithIssuedAtOffset(5 * time.Second)),
|
||||||
|
rp.WithHTTPClient(client),
|
||||||
|
rp.WithLogger(logger),
|
||||||
|
rp.WithSigningAlgsFromDiscovery(),
|
||||||
|
}
|
||||||
|
if clientSecret == "" {
|
||||||
|
options = append(options, rp.WithPKCE(cookieHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// One can add a logger to the context,
|
||||||
|
// pre-defining log attributes as required.
|
||||||
|
ctx := logging.ToContext(context.TODO(), logger)
|
||||||
|
provider, err := rp.NewRelyingPartyOIDC(ctx, issuer, clientID, clientSecret, redirectURI, scopeList, options...)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("error creating provider %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate some state (representing the state of the user in your application,
|
||||||
|
// e.g. the page where he was before sending him to login
|
||||||
|
state := func() string {
|
||||||
|
return uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
urlOptions := []rp.URLParamOpt{
|
||||||
|
rp.WithPromptURLParam("Welcome back!"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// register the AuthURLHandler at your preferred path.
|
||||||
|
// the AuthURLHandler creates the auth request and redirects the user to the auth server.
|
||||||
|
// including state handling with secure cookie and the possibility to use PKCE.
|
||||||
|
// Prompts can optionally be set to inform the server of
|
||||||
|
// any messages that need to be prompted back to the user.
|
||||||
|
http.Handle("/login", rp.AuthURLHandler(
|
||||||
|
state,
|
||||||
|
provider,
|
||||||
|
urlOptions...,
|
||||||
|
))
|
||||||
|
|
||||||
|
// for demonstration purposes the returned userinfo response is written as JSON object onto response
|
||||||
|
marshalUserinfo := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[*oidc.IDTokenClaims], state string, rp rp.RelyingParty, info *oidc.UserInfo) {
|
||||||
|
fmt.Println("access token", tokens.AccessToken)
|
||||||
|
fmt.Println("refresh token", tokens.RefreshToken)
|
||||||
|
fmt.Println("id token", tokens.IDToken)
|
||||||
|
|
||||||
|
data, err := json.Marshal(info)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("content-type", "application/json")
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// register the CodeExchangeHandler at the callbackPath
|
||||||
|
// the CodeExchangeHandler handles the auth response, creates the token request and calls the callback function
|
||||||
|
// with the returned tokens from the token endpoint
|
||||||
|
// in this example the callback function itself is wrapped by the UserinfoCallback which
|
||||||
|
// will call the Userinfo endpoint, check the sub and pass the info into the callback function
|
||||||
|
http.Handle(callbackPath, rp.CodeExchangeHandler(rp.UserinfoCallback(marshalUserinfo), provider))
|
||||||
|
|
||||||
|
// if you would use the callback without calling the userinfo endpoint, simply switch the callback handler for:
|
||||||
|
//
|
||||||
|
// http.Handle(callbackPath, rp.CodeExchangeHandler(marshalToken, provider))
|
||||||
|
|
||||||
|
// simple counter for request IDs
|
||||||
|
var counter atomic.Int64
|
||||||
|
// enable incomming request logging
|
||||||
|
mw := logging.Middleware(
|
||||||
|
logging.WithLogger(logger),
|
||||||
|
logging.WithGroup("server"),
|
||||||
|
logging.WithIDFunc(func() slog.Attr {
|
||||||
|
return slog.Int64("id", counter.Add(1))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: ":" + port,
|
||||||
|
Handler: mw(http.DefaultServeMux),
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Fatalf("HTTP server error: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("Stopped serving new connections.")
|
||||||
|
}()
|
||||||
|
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-sigChan
|
||||||
|
|
||||||
|
shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer shutdownRelease()
|
||||||
|
|
||||||
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||||
|
log.Fatalf("HTTP shutdown error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPAT(path string) string {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pat, err := io.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.Trim(string(pat), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createZitadelResources(apiURL, pat, domain, redirectURI, loginURL string) (string, string, error) {
|
||||||
|
projectID, err := CreateProject(apiURL, pat, domain)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return CreateApp(apiURL, pat, domain, projectID, redirectURI, loginURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
type project struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
type createProject struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ProjectRoleAssertion bool `json:"projectRoleAssertion"`
|
||||||
|
ProjectRoleCheck bool `json:"projectRoleCheck"`
|
||||||
|
HasProjectCheck bool `json:"hasProjectCheck"`
|
||||||
|
PrivateLabelingSetting string `json:"privateLabelingSetting"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateProject(apiURL, pat, domain string) (string, error) {
|
||||||
|
createProject := &createProject{
|
||||||
|
Name: "OIDC",
|
||||||
|
ProjectRoleAssertion: false,
|
||||||
|
ProjectRoleCheck: false,
|
||||||
|
HasProjectCheck: false,
|
||||||
|
PrivateLabelingSetting: "PRIVATE_LABELING_SETTING_UNSPECIFIED",
|
||||||
|
}
|
||||||
|
resp, err := doRequestWithHeaders(apiURL+"/management/v1/projects", pat, domain, createProject)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
p := new(project)
|
||||||
|
if err := json.Unmarshal(data, p); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fmt.Printf("projectID: %+v\n", p.ID)
|
||||||
|
return p.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type createApp struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
RedirectUris []string `json:"redirectUris"`
|
||||||
|
ResponseTypes []string `json:"responseTypes"`
|
||||||
|
GrantTypes []string `json:"grantTypes"`
|
||||||
|
AppType string `json:"appType"`
|
||||||
|
AuthMethodType string `json:"authMethodType"`
|
||||||
|
PostLogoutRedirectUris []string `json:"postLogoutRedirectUris"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
DevMode bool `json:"devMode"`
|
||||||
|
AccessTokenType string `json:"accessTokenType"`
|
||||||
|
AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion"`
|
||||||
|
IdTokenRoleAssertion bool `json:"idTokenRoleAssertion"`
|
||||||
|
IdTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion"`
|
||||||
|
ClockSkew string `json:"clockSkew"`
|
||||||
|
AdditionalOrigins []string `json:"additionalOrigins"`
|
||||||
|
SkipNativeAppSuccessPage bool `json:"skipNativeAppSuccessPage"`
|
||||||
|
BackChannelLogoutUri []string `json:"backChannelLogoutUri"`
|
||||||
|
LoginVersion version `json:"loginVersion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type version struct {
|
||||||
|
LoginV2 loginV2 `json:"loginV2"`
|
||||||
|
}
|
||||||
|
type loginV2 struct {
|
||||||
|
BaseUri string `json:"baseUri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type app struct {
|
||||||
|
ClientID string `json:"clientId"`
|
||||||
|
ClientSecret string `json:"clientSecret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateApp(apiURL, pat, domain, projectID string, redirectURI, loginURL string) (string, string, error) {
|
||||||
|
createApp := &createApp{
|
||||||
|
Name: "OIDC",
|
||||||
|
RedirectUris: []string{redirectURI},
|
||||||
|
ResponseTypes: []string{"OIDC_RESPONSE_TYPE_CODE"},
|
||||||
|
GrantTypes: []string{"OIDC_GRANT_TYPE_AUTHORIZATION_CODE"},
|
||||||
|
AppType: "OIDC_APP_TYPE_WEB",
|
||||||
|
AuthMethodType: "OIDC_AUTH_METHOD_TYPE_BASIC",
|
||||||
|
Version: "OIDC_VERSION_1_0",
|
||||||
|
DevMode: true,
|
||||||
|
AccessTokenType: "OIDC_TOKEN_TYPE_BEARER",
|
||||||
|
AccessTokenRoleAssertion: true,
|
||||||
|
IdTokenRoleAssertion: true,
|
||||||
|
IdTokenUserinfoAssertion: true,
|
||||||
|
ClockSkew: "1s",
|
||||||
|
SkipNativeAppSuccessPage: true,
|
||||||
|
LoginVersion: version{
|
||||||
|
LoginV2: loginV2{
|
||||||
|
BaseUri: loginURL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := doRequestWithHeaders(apiURL+"/management/v1/projects/"+projectID+"/apps/oidc", pat, domain, createApp)
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
a := new(app)
|
||||||
|
if err := json.Unmarshal(data, a); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return a.ClientID, a.ClientSecret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRequestWithHeaders(apiURL, pat, domain string, body any) (*http.Response, error) {
|
||||||
|
data, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, apiURL, io.NopCloser(bytes.NewReader(data)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values := http.Header{}
|
||||||
|
values.Add("Authorization", "Bearer "+pat)
|
||||||
|
values.Add("x-forwarded-host", domain)
|
||||||
|
values.Add("Content-Type", "application/json")
|
||||||
|
req.Header = values
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
22
acceptance/saml/docker-compose.yaml
Normal file
22
acceptance/saml/docker-compose.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
samlsp:
|
||||||
|
image: golang:1.24-alpine
|
||||||
|
container_name: samlsp
|
||||||
|
command: go run main.go
|
||||||
|
environment:
|
||||||
|
API_URL: 'http://localhost:8080'
|
||||||
|
API_DOMAIN: 'localhost:8080'
|
||||||
|
PAT_FILE: '/pat/zitadel-admin-sa.pat'
|
||||||
|
LOGIN_URL: 'http://localhost:3000'
|
||||||
|
IDP_URL: 'http://localhost:3000/saml/v2/metadata'
|
||||||
|
HOST: 'http://localhost'
|
||||||
|
PORT: '8001'
|
||||||
|
working_dir: /saml
|
||||||
|
ports:
|
||||||
|
- 8001:8001
|
||||||
|
volumes:
|
||||||
|
- "../pat:/pat"
|
||||||
|
- "./:/saml"
|
||||||
|
extra_hosts:
|
||||||
|
- "localhost:host-gateway"
|
||||||
|
|
18
acceptance/saml/go.mod
Normal file
18
acceptance/saml/go.mod
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module github.com/zitadel/typescript/acceptance/saml
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
require github.com/crewjam/saml v0.4.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beevik/etree v1.5.0 // indirect
|
||||||
|
github.com/crewjam/httperr v0.2.0 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
|
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/russellhaering/goxmldsig v1.5.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
|
golang.org/x/crypto v0.36.0 // indirect
|
||||||
|
)
|
38
acceptance/saml/go.sum
Normal file
38
acceptance/saml/go.sum
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs=
|
||||||
|
github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
|
||||||
|
github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
|
||||||
|
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
|
||||||
|
github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
|
||||||
|
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||||
|
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/russellhaering/goxmldsig v1.5.0 h1:AU2UkkYIUOTyZRbe08XMThaOCelArgvNfYapcmSjBNw=
|
||||||
|
github.com/russellhaering/goxmldsig v1.5.0/go.mod h1:x98CjQNFJcWfMxeOrMnMKg70lvDP6tE0nTaeUnjXDmk=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
266
acceptance/saml/main.go
Normal file
266
acceptance/saml/main.go
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/crewjam/saml/samlsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var keyPair = func() tls.Certificate {
|
||||||
|
cert := []byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDITCCAgmgAwIBAgIUKjAUmxsHO44X+/TKBNciPgNl1GEwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1wbGUuY29tMB4XDTI0MTIxOTEz
|
||||||
|
Mzc1MVoXDTI1MTIxOTEzMzc1MVowIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1w
|
||||||
|
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0QYuJsayILRI
|
||||||
|
hVT7G1DlitVSXnt1iw3gEXJZfe81Egz06fUbvXF6Yo1LJmwYpqe/rm+hf4FNUb8e
|
||||||
|
2O+LH2FieA9FkVe4P2gKOzw87A/KxvpV8stgNgl4LlqRCokbc1AzeE/NiLr5TcTD
|
||||||
|
RXm3DUcYxXxinprtDu2jftFysaOZmNAukvE/iL6qS3X6ggVEDDM7tY9n5FV2eJ4E
|
||||||
|
p0ImKfypi2aZYROxOK+v5x9ryFRMl4y07lMDvmtcV45uXYmfGNCgG9PNf91Kk/mh
|
||||||
|
JxEQbxycJwFoSi9XWljR8ahPdO11LXG7Dsj/RVbY8k2LdKNstl6Ae3aCpbe9u2Pj
|
||||||
|
vxYs1bVJuQIDAQABo1MwUTAdBgNVHQ4EFgQU+mRVN5HYJWgnpopReaLhf2cMcoYw
|
||||||
|
HwYDVR0jBBgwFoAU+mRVN5HYJWgnpopReaLhf2cMcoYwDwYDVR0TAQH/BAUwAwEB
|
||||||
|
/zANBgkqhkiG9w0BAQsFAAOCAQEABJpHVuc9tGhD04infRVlofvqXIUizTlOrjZX
|
||||||
|
vozW9pIhSWEHX8o+sJP8AMZLnrsdq+bm0HE0HvgYrw7Lb8pd4FpR46TkFHjeukoj
|
||||||
|
izqfgckjIBl2nwPGlynbKA0/U/rTCSxVt7XiAn+lgYUGIpOzNdk06/hRMitrMNB7
|
||||||
|
t2C97NseVC4b1ZgyFrozsefCfUmD8IJF0+XJ4Wzmsh0jRrI8koCtVmPYnKn6vw1b
|
||||||
|
cZprg/97CWHYrsavd406wOB60CMtYl83Q16ucOF1dretDFqJC5kY+aFLvuqfag2+
|
||||||
|
kIaoPV1MnGsxveQyyHdOsEatS5XOv/1OWcmnvePDPxcvb9jCcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`)
|
||||||
|
key := []byte(`-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRBi4mxrIgtEiF
|
||||||
|
VPsbUOWK1VJee3WLDeARcll97zUSDPTp9Ru9cXpijUsmbBimp7+ub6F/gU1Rvx7Y
|
||||||
|
74sfYWJ4D0WRV7g/aAo7PDzsD8rG+lXyy2A2CXguWpEKiRtzUDN4T82IuvlNxMNF
|
||||||
|
ebcNRxjFfGKemu0O7aN+0XKxo5mY0C6S8T+IvqpLdfqCBUQMMzu1j2fkVXZ4ngSn
|
||||||
|
QiYp/KmLZplhE7E4r6/nH2vIVEyXjLTuUwO+a1xXjm5diZ8Y0KAb081/3UqT+aEn
|
||||||
|
ERBvHJwnAWhKL1daWNHxqE907XUtcbsOyP9FVtjyTYt0o2y2XoB7doKlt727Y+O/
|
||||||
|
FizVtUm5AgMBAAECggEACak+l5f6Onj+u5vrjc4JyAaXW6ra6loSM9g8Uu3sHukW
|
||||||
|
plwoA7Pzp0u20CAxrP1Gpqw984/hSCCcb0Q2ItWMWLaC/YZni5W2WFnOyo3pzlPa
|
||||||
|
hmH4UNMT+ReCSfF/oW8w69QLcNEMjhfEu0i2iWBygIlA4SoRwC2Db6yEX7nLMwUB
|
||||||
|
6AICid9hfeACNRz/nq5ytdcHdmcB7Ptgb9jLiXr6RZw26g5AsRPHU3LdcyZAOXjP
|
||||||
|
aUHriHuHQFKAVkoEUxslvCB6ePCTCpB0bSAuzQbeGoY8fmvmNSCvJ1vrH5hiSUYp
|
||||||
|
Axtl5iNgFl5o9obb0eBYlY9x3pMSz0twdbCwfR7HAQKBgQDtWhmFm0NaJALoY+tq
|
||||||
|
lIIC0EOMSrcRIlgeXr6+g8womuDOMi5m/Nr5Mqt4mPOdP4HytrQb+a/ZmEm17KHh
|
||||||
|
mQb1vwH8ffirCBHbPNC1vwSNoxDKv9E6OysWlKiOzxPFSVZr3dKl2EMX6qi17n0l
|
||||||
|
LBrGXXaNPgYiHSmwBA5CZvvouQKBgQDhclGJfZfuoubQkUuz8yOA2uxalh/iUmQ/
|
||||||
|
G8ac6/w7dmnL9pXehqCWh06SeC3ZvW7yrf7IIGx4sTJji2FzQ+8Ta6pPELMyBEXr
|
||||||
|
1VirIFrlNVMlMQEbZcbzdzEhchM1RUpZJtl3b4amvH21UcRB69d9klcDRisKoFRm
|
||||||
|
k0P9QLHpAQKBgQDh5J9nphZa4u0ViYtTW1XFIbs3+R/0IbCl7tww67TRbF3KQL4i
|
||||||
|
7EHna88ALumkXf3qJvKRsXgoaqS0jSqgUAjst8ZHLQkOldaQxneIkezedDSWEisp
|
||||||
|
9YgTrJYjnHefiyXB8VL63jE0wPOiewEF8Mzmv6sFz+L8cq7rQ2Di16qmmQKBgQDH
|
||||||
|
bvCwVxkrMpJK2O2GH8U9fOzu6bUE6eviY/jb4mp8U7EdjGJhuuieoM2iBoxQ/SID
|
||||||
|
rmYftYcfcWlo4+juJZ99p5W+YcCTs3IDQPUyVOnzr6uA0Avxp6RKxhsBQj+5tTUj
|
||||||
|
Dpn77P3JzB7MYqvhwPcdD3LH46+5s8FWCFpx02RPAQKBgARbngtggfifatcsMC7n
|
||||||
|
lSv/FVLH7LYQAHdoW/EH5Be7FeeP+eQvGXwh1dgl+u0VZO8FvI8RwFganpBRR2Nc
|
||||||
|
ZSBRIb0fSUlTvIsckSWjpEvUJUomJXyi4PIZAfNvd9/u1uLInQiCDtObwb6hnLTU
|
||||||
|
FHHEZ+dR4eMaJp6PhNm8hu2O
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
`)
|
||||||
|
|
||||||
|
kp, err := tls.X509KeyPair(cert, key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
kp.Leaf, err = x509.ParseCertificate(kp.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return kp
|
||||||
|
}()
|
||||||
|
|
||||||
|
func hello(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "UserName"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
apiURL := os.Getenv("API_URL")
|
||||||
|
pat := readPAT(os.Getenv("PAT_FILE"))
|
||||||
|
domain := os.Getenv("API_DOMAIN")
|
||||||
|
loginURL := os.Getenv("LOGIN_URL")
|
||||||
|
idpURL := os.Getenv("IDP_URL")
|
||||||
|
host := os.Getenv("HOST")
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
|
||||||
|
idpMetadataURL, err := url.Parse(idpURL)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient,
|
||||||
|
*idpMetadataURL)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("idpMetadata: %+v\n", idpMetadata)
|
||||||
|
rootURL, err := url.Parse(host + ":" + port)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
samlSP, err := samlsp.New(samlsp.Options{
|
||||||
|
URL: *rootURL,
|
||||||
|
Key: keyPair.PrivateKey.(*rsa.PrivateKey),
|
||||||
|
Certificate: keyPair.Leaf,
|
||||||
|
IDPMetadata: idpMetadata,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: ":" + port,
|
||||||
|
}
|
||||||
|
app := http.HandlerFunc(hello)
|
||||||
|
http.Handle("/hello", samlSP.RequireAccount(app))
|
||||||
|
http.Handle("/saml/", samlSP)
|
||||||
|
go func() {
|
||||||
|
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Fatalf("HTTP server error: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("Stopped serving new connections.")
|
||||||
|
}()
|
||||||
|
|
||||||
|
metadata, err := xml.MarshalIndent(samlSP.ServiceProvider.Metadata(), "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := createZitadelResources(apiURL, pat, domain, metadata, loginURL); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-sigChan
|
||||||
|
|
||||||
|
shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer shutdownRelease()
|
||||||
|
|
||||||
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||||
|
log.Fatalf("HTTP shutdown error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPAT(path string) string {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pat, err := io.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.Trim(string(pat), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createZitadelResources(apiURL, pat, domain string, metadata []byte, loginURL string) error {
|
||||||
|
projectID, err := CreateProject(apiURL, pat, domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return CreateApp(apiURL, pat, domain, projectID, metadata, loginURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
type project struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
type createProject struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ProjectRoleAssertion bool `json:"projectRoleAssertion"`
|
||||||
|
ProjectRoleCheck bool `json:"projectRoleCheck"`
|
||||||
|
HasProjectCheck bool `json:"hasProjectCheck"`
|
||||||
|
PrivateLabelingSetting string `json:"privateLabelingSetting"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateProject(apiURL, pat, domain string) (string, error) {
|
||||||
|
createProject := &createProject{
|
||||||
|
Name: "SAML",
|
||||||
|
ProjectRoleAssertion: false,
|
||||||
|
ProjectRoleCheck: false,
|
||||||
|
HasProjectCheck: false,
|
||||||
|
PrivateLabelingSetting: "PRIVATE_LABELING_SETTING_UNSPECIFIED",
|
||||||
|
}
|
||||||
|
resp, err := doRequestWithHeaders(apiURL+"/management/v1/projects", pat, domain, createProject)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
p := new(project)
|
||||||
|
if err := json.Unmarshal(data, p); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return p.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type createApp struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MetadataXml string `json:"metadataXml"`
|
||||||
|
LoginVersion version `json:"loginVersion"`
|
||||||
|
}
|
||||||
|
type version struct {
|
||||||
|
LoginV2 loginV2 `json:"loginV2"`
|
||||||
|
}
|
||||||
|
type loginV2 struct {
|
||||||
|
BaseUri string `json:"baseUri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateApp(apiURL, pat, domain, projectID string, spMetadata []byte, loginURL string) error {
|
||||||
|
encoded := make([]byte, base64.URLEncoding.EncodedLen(len(spMetadata)))
|
||||||
|
base64.URLEncoding.Encode(encoded, spMetadata)
|
||||||
|
|
||||||
|
createApp := &createApp{
|
||||||
|
Name: "SAML",
|
||||||
|
MetadataXml: string(encoded),
|
||||||
|
LoginVersion: version{
|
||||||
|
LoginV2: loginV2{
|
||||||
|
BaseUri: loginURL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := doRequestWithHeaders(apiURL+"/management/v1/projects/"+projectID+"/apps/saml", pat, domain, createApp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRequestWithHeaders(apiURL, pat, domain string, body any) (*http.Response, error) {
|
||||||
|
data, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, apiURL, io.NopCloser(bytes.NewReader(data)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values := http.Header{}
|
||||||
|
values.Add("Authorization", "Bearer "+pat)
|
||||||
|
values.Add("x-forwarded-host", domain)
|
||||||
|
values.Add("Content-Type", "application/json")
|
||||||
|
req.Header = values
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
@@ -17,6 +17,40 @@ if [ -z "${PAT}" ]; then
|
|||||||
PAT=$(cat ${PAT_FILE})
|
PAT=$(cat ${PAT_FILE})
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#################################################################
|
||||||
|
# ServiceAccount as Login Client
|
||||||
|
#################################################################
|
||||||
|
|
||||||
|
SERVICEACCOUNT_RESPONSE=$(curl -s --request POST \
|
||||||
|
--url "${ZITADEL_API_INTERNAL_URL}/management/v1/users/machine" \
|
||||||
|
--header "Authorization: Bearer ${PAT}" \
|
||||||
|
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
-d "{\"userName\": \"login\", \"name\": \"Login v2\", \"description\": \"Serviceaccount for Login v2\", \"accessTokenType\": \"ACCESS_TOKEN_TYPE_BEARER\"}")
|
||||||
|
echo "Received ServiceAccount response: ${SERVICEACCOUNT_RESPONSE}"
|
||||||
|
|
||||||
|
SERVICEACCOUNT_ID=$(echo ${SERVICEACCOUNT_RESPONSE} | jq -r '. | .userId')
|
||||||
|
echo "Received ServiceAccount ID: ${SERVICEACCOUNT_ID}"
|
||||||
|
|
||||||
|
MEMBER_RESPONSE=$(curl -s --request POST \
|
||||||
|
--url "${ZITADEL_API_INTERNAL_URL}/admin/v1/members" \
|
||||||
|
--header "Authorization: Bearer ${PAT}" \
|
||||||
|
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
-d "{\"userId\": \"${SERVICEACCOUNT_ID}\", \"roles\": [\"IAM_LOGIN_CLIENT\"]}")
|
||||||
|
echo "Received Member response: ${MEMBER_RESPONSE}"
|
||||||
|
|
||||||
|
SA_PAT_RESPONSE=$(curl -s --request POST \
|
||||||
|
--url "${ZITADEL_API_INTERNAL_URL}/management/v1/users/${SERVICEACCOUNT_ID}/pats" \
|
||||||
|
--header "Authorization: Bearer ${PAT}" \
|
||||||
|
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
-d "{\"expirationDate\": \"2519-04-01T08:45:00.000000Z\"}")
|
||||||
|
echo "Received Member response: ${MEMBER_RESPONSE}"
|
||||||
|
|
||||||
|
SA_PAT=$(echo ${SA_PAT_RESPONSE} | jq -r '. | .token')
|
||||||
|
echo "Received ServiceAccount Token: ${SA_PAT}"
|
||||||
|
|
||||||
#################################################################
|
#################################################################
|
||||||
# Environment files
|
# Environment files
|
||||||
#################################################################
|
#################################################################
|
||||||
@@ -27,7 +61,8 @@ WRITE_TEST_ENVIRONMENT_FILE=${WRITE_TEST_ENVIRONMENT_FILE:-$(dirname "$0")/../ac
|
|||||||
echo "Writing environment file to ${WRITE_TEST_ENVIRONMENT_FILE} when done."
|
echo "Writing environment file to ${WRITE_TEST_ENVIRONMENT_FILE} when done."
|
||||||
|
|
||||||
echo "ZITADEL_API_URL=${ZITADEL_API_URL}
|
echo "ZITADEL_API_URL=${ZITADEL_API_URL}
|
||||||
ZITADEL_SERVICE_USER_TOKEN=${PAT}
|
ZITADEL_SERVICE_USER_TOKEN=${SA_PAT}
|
||||||
|
ZITADEL_ADMIN_TOKEN=${PAT}
|
||||||
SINK_NOTIFICATION_URL=${SINK_NOTIFICATION_URL}
|
SINK_NOTIFICATION_URL=${SINK_NOTIFICATION_URL}
|
||||||
EMAIL_VERIFICATION=true
|
EMAIL_VERIFICATION=true
|
||||||
DEBUG=true"| tee "${WRITE_ENVIRONMENT_FILE}" "${WRITE_TEST_ENVIRONMENT_FILE}" > /dev/null
|
DEBUG=true"| tee "${WRITE_ENVIRONMENT_FILE}" "${WRITE_TEST_ENVIRONMENT_FILE}" > /dev/null
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
module github.com/zitadel/typescript/acceptance/sink
|
module github.com/zitadel/typescript/acceptance/sink
|
||||||
|
|
||||||
go 1.22.6
|
go 1.24.0
|
||||||
|
@@ -29,7 +29,7 @@ export async function passwordScreen(page: Page, password: string) {
|
|||||||
|
|
||||||
export async function passwordScreenExpect(page: Page, password: string) {
|
export async function passwordScreenExpect(page: Page, password: string) {
|
||||||
await expect(page.getByTestId(passwordField)).toHaveValue(password);
|
await expect(page.getByTestId(passwordField)).toHaveValue(password);
|
||||||
await expect(page.getByTestId("error").locator("div")).toContainText("Could not verify password");
|
await expect(page.getByTestId("error").locator("div")).toContainText("Failed to authenticate.");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function changePasswordScreenExpect(
|
export async function changePasswordScreenExpect(
|
||||||
|
5
acceptance/tests/select-account.ts
Normal file
5
acceptance/tests/select-account.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Page } from "@playwright/test";
|
||||||
|
|
||||||
|
export async function selectNewAccount(page: Page) {
|
||||||
|
await page.getByRole("link", { name: "Add another account" }).click();
|
||||||
|
}
|
@@ -53,7 +53,7 @@ async function deleteCall(url: string) {
|
|||||||
try {
|
try {
|
||||||
const response = await axios.delete(url, {
|
const response = await axios.delete(url, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
Authorization: `Bearer ${process.env.ZITADEL_ADMIN_TOKEN}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ async function listCall(url: string, data: any): Promise<any> {
|
|||||||
const response = await axios.post(url, data, {
|
const response = await axios.post(url, data, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
Authorization: `Bearer ${process.env.ZITADEL_ADMIN_TOKEN}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ async function pushCall(url: string, data: any) {
|
|||||||
const response = await axios.post(url, data, {
|
const response = await axios.post(url, data, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
Authorization: `Bearer ${process.env.ZITADEL_ADMIN_TOKEN}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
196
apps/login/locales/pl.json
Normal file
196
apps/login/locales/pl.json
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"back": "Powrót"
|
||||||
|
},
|
||||||
|
"accounts": {
|
||||||
|
"title": "Konta",
|
||||||
|
"description": "Wybierz konto, którego chcesz użyć.",
|
||||||
|
"addAnother": "Dodaj kolejne konto",
|
||||||
|
"noResults": "Nie znaleziono kont"
|
||||||
|
},
|
||||||
|
"loginname": {
|
||||||
|
"title": "Witamy ponownie!",
|
||||||
|
"description": "Wprowadź dane logowania.",
|
||||||
|
"register": "Zarejestruj nowego użytkownika"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Hasło",
|
||||||
|
"description": "Wprowadź swoje hasło.",
|
||||||
|
"resetPassword": "Zresetuj hasło",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Ustaw hasło",
|
||||||
|
"description": "Ustaw hasło dla swojego konta",
|
||||||
|
"codeSent": "Kod został wysłany na twój adres e-mail.",
|
||||||
|
"noCodeReceived": "Nie otrzymałeś kodu?",
|
||||||
|
"resend": "Wyślij kod ponownie",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
},
|
||||||
|
"change": {
|
||||||
|
"title": "Zmień hasło",
|
||||||
|
"description": "Ustaw nowe hasło dla swojego konta",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idp": {
|
||||||
|
"title": "Zaloguj się za pomocą SSO",
|
||||||
|
"description": "Wybierz jednego z poniższych dostawców, aby się zalogować",
|
||||||
|
"signInWithApple": "Zaloguj się przez Apple",
|
||||||
|
"signInWithGoogle": "Zaloguj się przez Google",
|
||||||
|
"signInWithAzureAD": "Zaloguj się przez AzureAD",
|
||||||
|
"signInWithGithub": "Zaloguj się przez GitHub",
|
||||||
|
"signInWithGitlab": "Zaloguj się przez GitLab",
|
||||||
|
"loginSuccess": {
|
||||||
|
"title": "Logowanie udane",
|
||||||
|
"description": "Zostałeś pomyślnie zalogowany!"
|
||||||
|
},
|
||||||
|
"linkingSuccess": {
|
||||||
|
"title": "Konto powiązane",
|
||||||
|
"description": "Pomyślnie powiązałeś swoje konto!"
|
||||||
|
},
|
||||||
|
"registerSuccess": {
|
||||||
|
"title": "Rejestracja udana",
|
||||||
|
"description": "Pomyślnie się zarejestrowałeś!"
|
||||||
|
},
|
||||||
|
"loginError": {
|
||||||
|
"title": "Logowanie nieudane",
|
||||||
|
"description": "Wystąpił błąd podczas próby logowania."
|
||||||
|
},
|
||||||
|
"linkingError": {
|
||||||
|
"title": "Powiązanie konta nie powiodło się",
|
||||||
|
"description": "Wystąpił błąd podczas próby powiązania konta."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Zweryfikuj swoją tożsamość",
|
||||||
|
"description": "Wybierz jeden z poniższych sposobów weryfikacji.",
|
||||||
|
"noResults": "Nie znaleziono dostępnych metod uwierzytelniania dwuskładnikowego."
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Skonfiguruj uwierzytelnianie dwuskładnikowe",
|
||||||
|
"description": "Wybierz jedną z poniższych metod drugiego czynnika.",
|
||||||
|
"skip": "Pomiń"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"otp": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Zweryfikuj uwierzytelnianie dwuskładnikowe",
|
||||||
|
"totpDescription": "Wprowadź kod z aplikacji uwierzytelniającej.",
|
||||||
|
"smsDescription": "Wprowadź kod otrzymany SMS-em.",
|
||||||
|
"emailDescription": "Wprowadź kod otrzymany e-mailem.",
|
||||||
|
"noCodeReceived": "Nie otrzymałeś kodu?",
|
||||||
|
"resendCode": "Wyślij kod ponownie",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Skonfiguruj uwierzytelnianie dwuskładnikowe",
|
||||||
|
"totpDescription": "Zeskanuj kod QR za pomocą aplikacji uwierzytelniającej.",
|
||||||
|
"smsDescription": "Wprowadź swój numer telefonu, aby otrzymać kod SMS-em.",
|
||||||
|
"emailDescription": "Wprowadź swój adres e-mail, aby otrzymać kod e-mailem.",
|
||||||
|
"totpRegisterDescription": "Zeskanuj kod QR lub otwórz adres URL ręcznie.",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"passkey": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Uwierzytelnij się za pomocą klucza dostępu",
|
||||||
|
"description": "Twoje urządzenie poprosi o użycie odcisku palca, rozpoznawania twarzy lub blokady ekranu.",
|
||||||
|
"usePassword": "Użyj hasła",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Skonfiguruj klucz dostępu",
|
||||||
|
"description": "Twoje urządzenie poprosi o użycie odcisku palca, rozpoznawania twarzy lub blokady ekranu.",
|
||||||
|
"info": {
|
||||||
|
"description": "Klucz dostępu to metoda uwierzytelniania na urządzeniu, wykorzystująca np. odcisk palca, Apple FaceID lub podobne rozwiązania.",
|
||||||
|
"link": "Uwierzytelnianie bez hasła"
|
||||||
|
},
|
||||||
|
"skip": "Pomiń",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"u2f": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Zweryfikuj uwierzytelnianie dwuskładnikowe",
|
||||||
|
"description": "Zweryfikuj swoje konto za pomocą urządzenia."
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Skonfiguruj uwierzytelnianie dwuskładnikowe",
|
||||||
|
"description": "Skonfiguruj urządzenie jako dodatkowy czynnik uwierzytelniania.",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"methods": {
|
||||||
|
"passkey": "Klucz dostępu",
|
||||||
|
"password": "Hasło"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"title": "Rejestracja wyłączona",
|
||||||
|
"description": "Rejestracja jest wyłączona. Skontaktuj się z administratorem."
|
||||||
|
},
|
||||||
|
"missingdata": {
|
||||||
|
"title": "Brak danych",
|
||||||
|
"description": "Podaj e-mail, imię i nazwisko, aby się zarejestrować."
|
||||||
|
},
|
||||||
|
"title": "Rejestracja",
|
||||||
|
"description": "Utwórz konto ZITADEL.",
|
||||||
|
"selectMethod": "Wybierz metodę uwierzytelniania, której chcesz użyć",
|
||||||
|
"agreeTo": "Aby się zarejestrować, musisz zaakceptować warunki korzystania",
|
||||||
|
"termsOfService": "Regulamin",
|
||||||
|
"privacyPolicy": "Polityka prywatności",
|
||||||
|
"submit": "Kontynuuj",
|
||||||
|
"password": {
|
||||||
|
"title": "Ustaw hasło",
|
||||||
|
"description": "Ustaw hasło dla swojego konta",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invite": {
|
||||||
|
"title": "Zaproś użytkownika",
|
||||||
|
"description": "Podaj adres e-mail oraz imię i nazwisko użytkownika, którego chcesz zaprosić.",
|
||||||
|
"info": "Użytkownik otrzyma e-mail z dalszymi instrukcjami.",
|
||||||
|
"notAllowed": "Twoje ustawienia nie pozwalają na zapraszanie użytkowników.",
|
||||||
|
"submit": "Kontynuuj",
|
||||||
|
"success": {
|
||||||
|
"title": "Użytkownik zaproszony",
|
||||||
|
"description": "E-mail został pomyślnie wysłany.",
|
||||||
|
"verified": "Użytkownik został zaproszony i już zweryfikował swój e-mail.",
|
||||||
|
"notVerifiedYet": "Użytkownik został zaproszony. Otrzyma e-mail z dalszymi instrukcjami.",
|
||||||
|
"submit": "Zaproś kolejnego użytkownika"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"signedin": {
|
||||||
|
"title": "Witaj {user}!",
|
||||||
|
"description": "Jesteś zalogowany.",
|
||||||
|
"continue": "Kontynuuj"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"userIdMissing": "Nie podano identyfikatora użytkownika!",
|
||||||
|
"success": "Użytkownik został pomyślnie zweryfikowany.",
|
||||||
|
"setupAuthenticator": "Skonfiguruj uwierzytelnianie",
|
||||||
|
"verify": {
|
||||||
|
"title": "Zweryfikuj użytkownika",
|
||||||
|
"description": "Wprowadź kod z wiadomości weryfikacyjnej.",
|
||||||
|
"noCodeReceived": "Nie otrzymałeś kodu?",
|
||||||
|
"resendCode": "Wyślij kod ponownie",
|
||||||
|
"submit": "Kontynuuj"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authenticator": {
|
||||||
|
"title": "Wybierz metodę uwierzytelniania",
|
||||||
|
"description": "Wybierz metodę, której chcesz użyć do uwierzytelnienia.",
|
||||||
|
"noMethodsAvailable": "Brak dostępnych metod uwierzytelniania",
|
||||||
|
"allSetup": "Już skonfigurowałeś metodę uwierzytelniania!",
|
||||||
|
"linkWithIDP": "lub połącz z dostawcą tożsamości"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"unknownContext": "Nie udało się pobrać kontekstu użytkownika. Upewnij się, że najpierw wprowadziłeś nazwę użytkownika lub podałeś login jako parametr wyszukiwania.",
|
||||||
|
"sessionExpired": "Twoja sesja wygasła. Zaloguj się ponownie.",
|
||||||
|
"failedLoading": "Nie udało się załadować danych. Spróbuj ponownie.",
|
||||||
|
"tryagain": "Spróbuj ponownie"
|
||||||
|
}
|
||||||
|
}
|
196
apps/login/locales/ru.json
Normal file
196
apps/login/locales/ru.json
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"back": "Назад"
|
||||||
|
},
|
||||||
|
"accounts": {
|
||||||
|
"title": "Аккаунты",
|
||||||
|
"description": "Выберите аккаунт, который хотите использовать.",
|
||||||
|
"addAnother": "Добавить другой аккаунт",
|
||||||
|
"noResults": "Аккаунты не найдены"
|
||||||
|
},
|
||||||
|
"loginname": {
|
||||||
|
"title": "С возвращением!",
|
||||||
|
"description": "Введите свои данные для входа.",
|
||||||
|
"register": "Зарегистрировать нового пользователя"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Пароль",
|
||||||
|
"description": "Введите ваш пароль.",
|
||||||
|
"resetPassword": "Сбросить пароль",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Установить пароль",
|
||||||
|
"description": "Установите пароль для вашего аккаунта",
|
||||||
|
"codeSent": "Код отправлен на ваш адрес электронной почты.",
|
||||||
|
"noCodeReceived": "Не получили код?",
|
||||||
|
"resend": "Отправить код повторно",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
},
|
||||||
|
"change": {
|
||||||
|
"title": "Изменить пароль",
|
||||||
|
"description": "Установите пароль для вашего аккаунта",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idp": {
|
||||||
|
"title": "Войти через SSO",
|
||||||
|
"description": "Выберите одного из провайдеров для входа",
|
||||||
|
"signInWithApple": "Войти через Apple",
|
||||||
|
"signInWithGoogle": "Войти через Google",
|
||||||
|
"signInWithAzureAD": "Войти через AzureAD",
|
||||||
|
"signInWithGithub": "Войти через GitHub",
|
||||||
|
"signInWithGitlab": "Войти через GitLab",
|
||||||
|
"loginSuccess": {
|
||||||
|
"title": "Вход выполнен успешно",
|
||||||
|
"description": "Вы успешно вошли в систему!"
|
||||||
|
},
|
||||||
|
"linkingSuccess": {
|
||||||
|
"title": "Аккаунт привязан",
|
||||||
|
"description": "Аккаунт успешно привязан!"
|
||||||
|
},
|
||||||
|
"registerSuccess": {
|
||||||
|
"title": "Регистрация завершена",
|
||||||
|
"description": "Вы успешно зарегистрировались!"
|
||||||
|
},
|
||||||
|
"loginError": {
|
||||||
|
"title": "Ошибка входа",
|
||||||
|
"description": "Произошла ошибка при попытке входа."
|
||||||
|
},
|
||||||
|
"linkingError": {
|
||||||
|
"title": "Ошибка привязки аккаунта",
|
||||||
|
"description": "Произошла ошибка при попытке привязать аккаунт."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Подтвердите вашу личность",
|
||||||
|
"description": "Выберите один из следующих факторов.",
|
||||||
|
"noResults": "Нет доступных методов двухфакторной аутентификации"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Настройка двухфакторной аутентификации",
|
||||||
|
"description": "Выберите один из следующих методов.",
|
||||||
|
"skip": "Пропустить"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"otp": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Подтверждение 2FA",
|
||||||
|
"totpDescription": "Введите код из приложения-аутентификатора.",
|
||||||
|
"smsDescription": "Введите код, полученный по SMS.",
|
||||||
|
"emailDescription": "Введите код, полученный по email.",
|
||||||
|
"noCodeReceived": "Не получили код?",
|
||||||
|
"resendCode": "Отправить код повторно",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Настройка двухфакторной аутентификации",
|
||||||
|
"totpDescription": "Отсканируйте QR-код в приложении-аутентификаторе.",
|
||||||
|
"smsDescription": "Введите номер телефона для получения кода по SMS.",
|
||||||
|
"emailDescription": "Введите email для получения кода.",
|
||||||
|
"totpRegisterDescription": "Отсканируйте QR-код или перейдите по ссылке вручную.",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"passkey": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Аутентификация с помощью пасскей",
|
||||||
|
"description": "Устройство запросит отпечаток пальца, лицо или экранный замок",
|
||||||
|
"usePassword": "Использовать пароль",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Настройка пасскей",
|
||||||
|
"description": "Устройство запросит отпечаток пальца, лицо или экранный замок",
|
||||||
|
"info": {
|
||||||
|
"description": "Пасскей — метод аутентификации через устройство (отпечаток пальца, Apple FaceID и аналоги).",
|
||||||
|
"link": "Аутентификация без пароля"
|
||||||
|
},
|
||||||
|
"skip": "Пропустить",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"u2f": {
|
||||||
|
"verify": {
|
||||||
|
"title": "Подтверждение 2FA",
|
||||||
|
"description": "Подтвердите аккаунт с помощью устройства."
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"title": "Настройка двухфакторной аутентификации",
|
||||||
|
"description": "Настройте устройство как второй фактор.",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"methods": {
|
||||||
|
"passkey": "Пасскей",
|
||||||
|
"password": "Пароль"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"title": "Регистрация отключена",
|
||||||
|
"description": "Регистрация недоступна. Обратитесь к администратору."
|
||||||
|
},
|
||||||
|
"missingdata": {
|
||||||
|
"title": "Недостаточно данных",
|
||||||
|
"description": "Укажите email, имя и фамилию для регистрации."
|
||||||
|
},
|
||||||
|
"title": "Регистрация",
|
||||||
|
"description": "Создайте свой аккаунт ZITADEL.",
|
||||||
|
"selectMethod": "Выберите метод аутентификации",
|
||||||
|
"agreeTo": "Для регистрации необходимо принять условия:",
|
||||||
|
"termsOfService": "Условия использования",
|
||||||
|
"privacyPolicy": "Политика конфиденциальности",
|
||||||
|
"submit": "Продолжить",
|
||||||
|
"password": {
|
||||||
|
"title": "Установить пароль",
|
||||||
|
"description": "Установите пароль для вашего аккаунта",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invite": {
|
||||||
|
"title": "Пригласить пользователя",
|
||||||
|
"description": "Укажите email и имя пользователя для приглашения.",
|
||||||
|
"info": "Пользователь получит email с инструкциями.",
|
||||||
|
"notAllowed": "Ваши настройки не позволяют приглашать пользователей.",
|
||||||
|
"submit": "Продолжить",
|
||||||
|
"success": {
|
||||||
|
"title": "Пользователь приглашён",
|
||||||
|
"description": "Письмо успешно отправлено.",
|
||||||
|
"verified": "Пользователь приглашён и уже подтвердил email.",
|
||||||
|
"notVerifiedYet": "Пользователь приглашён. Он получит email с инструкциями.",
|
||||||
|
"submit": "Пригласить другого пользователя"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"signedin": {
|
||||||
|
"title": "Добро пожаловать, {user}!",
|
||||||
|
"description": "Вы вошли в систему.",
|
||||||
|
"continue": "Продолжить"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"userIdMissing": "Не указан userId!",
|
||||||
|
"success": "Пользователь успешно подтверждён.",
|
||||||
|
"setupAuthenticator": "Настроить аутентификатор",
|
||||||
|
"verify": {
|
||||||
|
"title": "Подтверждение пользователя",
|
||||||
|
"description": "Введите код из письма подтверждения.",
|
||||||
|
"noCodeReceived": "Не получили код?",
|
||||||
|
"resendCode": "Отправить код повторно",
|
||||||
|
"submit": "Продолжить"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authenticator": {
|
||||||
|
"title": "Выбор метода аутентификации",
|
||||||
|
"description": "Выберите предпочитаемый метод аутентификации",
|
||||||
|
"noMethodsAvailable": "Нет доступных методов аутентификации",
|
||||||
|
"allSetup": "Аутентификатор уже настроен!",
|
||||||
|
"linkWithIDP": "или привязать через Identity Provider"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"unknownContext": "Не удалось получить контекст пользователя. Укажите имя пользователя или loginName в параметрах поиска.",
|
||||||
|
"sessionExpired": "Ваша сессия истекла. Войдите снова.",
|
||||||
|
"failedLoading": "Ошибка загрузки данных. Попробуйте ещё раз.",
|
||||||
|
"tryagain": "Попробовать снова"
|
||||||
|
}
|
||||||
|
}
|
@@ -46,8 +46,9 @@
|
|||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"jose": "^5.3.0",
|
"jose": "^5.3.0",
|
||||||
|
"lucide-react": "0.469.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"next": "15.2.0-canary.33",
|
"next": "15.3.1-canary.9",
|
||||||
"next-intl": "^3.25.1",
|
"next-intl": "^3.25.1",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
"nice-grpc": "2.0.1",
|
"nice-grpc": "2.0.1",
|
||||||
|
@@ -157,17 +157,20 @@ export default async function Page(props: {
|
|||||||
></ChooseAuthenticatorToSetup>
|
></ChooseAuthenticatorToSetup>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="py-3 flex flex-col">
|
|
||||||
<p className="ztdl-p text-center">{t("linkWithIDP")}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{loginSettings?.allowExternalIdp && identityProviders && (
|
{loginSettings?.allowExternalIdp && identityProviders && (
|
||||||
<SignInWithIdp
|
<>
|
||||||
identityProviders={identityProviders}
|
{identityProviders.length && (
|
||||||
requestId={requestId}
|
<div className="py-3 flex flex-col">
|
||||||
organization={sessionWithData.factors?.user?.organizationId}
|
<p className="ztdl-p text-center">{t("linkWithIDP")}</p>
|
||||||
linkOnly={true} // tell the callback function to just link the IDP and not login, to get an error when user is already available
|
</div>
|
||||||
></SignInWithIdp>
|
)}
|
||||||
|
<SignInWithIdp
|
||||||
|
identityProviders={identityProviders}
|
||||||
|
requestId={requestId}
|
||||||
|
organization={sessionWithData.factors?.user?.organizationId}
|
||||||
|
linkOnly={true} // tell the callback function to just link the IDP and not login, to get an error when user is already available
|
||||||
|
></SignInWithIdp>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mt-8 flex w-full flex-row items-center">
|
<div className="mt-8 flex w-full flex-row items-center">
|
||||||
|
@@ -4,7 +4,7 @@ import { linkingFailed } from "@/components/idps/pages/linking-failed";
|
|||||||
import { linkingSuccess } from "@/components/idps/pages/linking-success";
|
import { linkingSuccess } from "@/components/idps/pages/linking-success";
|
||||||
import { loginFailed } from "@/components/idps/pages/login-failed";
|
import { loginFailed } from "@/components/idps/pages/login-failed";
|
||||||
import { loginSuccess } from "@/components/idps/pages/login-success";
|
import { loginSuccess } from "@/components/idps/pages/login-success";
|
||||||
import { idpTypeToIdentityProviderType, PROVIDER_MAPPING } from "@/lib/idp";
|
import { idpTypeToIdentityProviderType } from "@/lib/idp";
|
||||||
import { getServiceUrlFromHeaders } from "@/lib/service";
|
import { getServiceUrlFromHeaders } from "@/lib/service";
|
||||||
import {
|
import {
|
||||||
addHuman,
|
addHuman,
|
||||||
@@ -19,10 +19,7 @@ import {
|
|||||||
import { create } from "@zitadel/client";
|
import { create } from "@zitadel/client";
|
||||||
import { AutoLinkingOption } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
|
import { AutoLinkingOption } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
|
||||||
import { OrganizationSchema } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
import { OrganizationSchema } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
||||||
import {
|
import { AddHumanUserRequestSchema } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||||
AddHumanUserRequest,
|
|
||||||
AddHumanUserRequestSchema,
|
|
||||||
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
|
||||||
import { getLocale, getTranslations } from "next-intl/server";
|
import { getLocale, getTranslations } from "next-intl/server";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
|
|
||||||
@@ -58,6 +55,7 @@ export default async function Page(props: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { idpInformation, userId } = intent;
|
const { idpInformation, userId } = intent;
|
||||||
|
let { addHumanUser } = intent;
|
||||||
|
|
||||||
// sign in user. If user should be linked continue
|
// sign in user. If user should be linked continue
|
||||||
if (userId && !link) {
|
if (userId && !link) {
|
||||||
@@ -124,7 +122,7 @@ export default async function Page(props: {
|
|||||||
// search for potential user via username, then link
|
// search for potential user via username, then link
|
||||||
if (options?.isLinkingAllowed) {
|
if (options?.isLinkingAllowed) {
|
||||||
let foundUser;
|
let foundUser;
|
||||||
const email = PROVIDER_MAPPING[providerType](idpInformation).email?.email;
|
const email = addHumanUser?.email?.email;
|
||||||
|
|
||||||
if (options.autoLinking === AutoLinkingOption.EMAIL && email) {
|
if (options.autoLinking === AutoLinkingOption.EMAIL && email) {
|
||||||
foundUser = await listUsers({ serviceUrl, email }).then((response) => {
|
foundUser = await listUsers({ serviceUrl, email }).then((response) => {
|
||||||
@@ -180,16 +178,14 @@ export default async function Page(props: {
|
|||||||
|
|
||||||
if (options?.isCreationAllowed && options.isAutoCreation) {
|
if (options?.isCreationAllowed && options.isAutoCreation) {
|
||||||
let orgToRegisterOn: string | undefined = organization;
|
let orgToRegisterOn: string | undefined = organization;
|
||||||
|
let newUser;
|
||||||
let userData: AddHumanUserRequest =
|
|
||||||
PROVIDER_MAPPING[providerType](idpInformation);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!orgToRegisterOn &&
|
!orgToRegisterOn &&
|
||||||
userData.username && // username or email?
|
addHumanUser?.username && // username or email?
|
||||||
ORG_SUFFIX_REGEX.test(userData.username)
|
ORG_SUFFIX_REGEX.test(addHumanUser.username)
|
||||||
) {
|
) {
|
||||||
const matched = ORG_SUFFIX_REGEX.exec(userData.username);
|
const matched = ORG_SUFFIX_REGEX.exec(addHumanUser.username);
|
||||||
const suffix = matched?.[1] ?? "";
|
const suffix = matched?.[1] ?? "";
|
||||||
|
|
||||||
// this just returns orgs where the suffix is set as primary domain
|
// this just returns orgs where the suffix is set as primary domain
|
||||||
@@ -209,21 +205,21 @@ export default async function Page(props: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orgToRegisterOn) {
|
if (addHumanUser && orgToRegisterOn) {
|
||||||
const organizationSchema = create(OrganizationSchema, {
|
const organizationSchema = create(OrganizationSchema, {
|
||||||
org: { case: "orgId", value: orgToRegisterOn },
|
org: { case: "orgId", value: orgToRegisterOn },
|
||||||
});
|
});
|
||||||
|
|
||||||
userData = create(AddHumanUserRequestSchema, {
|
const addHumanUserWithOrganization = create(AddHumanUserRequestSchema, {
|
||||||
...userData,
|
...addHumanUser,
|
||||||
organization: organizationSchema,
|
organization: organizationSchema,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const newUser = await addHuman({
|
newUser = await addHuman({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
request: userData,
|
request: addHumanUserWithOrganization,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (newUser) {
|
if (newUser) {
|
||||||
return (
|
return (
|
||||||
|
43
apps/login/src/app/(login)/saml-post/page.tsx
Normal file
43
apps/login/src/app/(login)/saml-post/page.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
export default function SamlPost() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const url = searchParams.get("url");
|
||||||
|
const relayState = searchParams.get("RelayState");
|
||||||
|
const samlResponse = searchParams.get("SAMLResponse");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Automatically submit the form after rendering
|
||||||
|
const form = document.getElementById("samlForm") as HTMLFormElement;
|
||||||
|
if (form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!url || !relayState || !samlResponse) {
|
||||||
|
return (
|
||||||
|
<p className="text-center">Missing required parameters for SAML POST.</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charSet="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Redirecting...</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form id="samlForm" action={url} method="POST">
|
||||||
|
<input type="hidden" name="RelayState" value={relayState} />
|
||||||
|
<input type="hidden" name="SAMLResponse" value={samlResponse} />
|
||||||
|
</form>
|
||||||
|
<p>Redirecting...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
@@ -473,40 +473,16 @@ export async function GET(request: NextRequest) {
|
|||||||
if (url && binding.case === "redirect") {
|
if (url && binding.case === "redirect") {
|
||||||
return NextResponse.redirect(url);
|
return NextResponse.redirect(url);
|
||||||
} else if (url && binding.case === "post") {
|
} else if (url && binding.case === "post") {
|
||||||
// Create form data after SAML standard
|
const redirectUrl = constructUrl(request, "/saml-post");
|
||||||
const formData = {
|
|
||||||
RelayState: binding.value.relayState,
|
|
||||||
SAMLResponse: binding.value.samlResponse,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert form data to URL-encoded string
|
redirectUrl.searchParams.set("url", url);
|
||||||
const formBody = Object.entries(formData)
|
redirectUrl.searchParams.set("RelayState", binding.value.relayState);
|
||||||
.map(
|
redirectUrl.searchParams.set(
|
||||||
([key, value]) =>
|
"SAMLResponse",
|
||||||
encodeURIComponent(key) + "=" + encodeURIComponent(value),
|
binding.value.samlResponse,
|
||||||
)
|
);
|
||||||
.join("&");
|
|
||||||
|
|
||||||
// Make a POST request to the external URL with the form data
|
return NextResponse.redirect(redirectUrl.toString());
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
|
||||||
},
|
|
||||||
body: formBody,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle the response from the external URL
|
|
||||||
if (response.ok) {
|
|
||||||
return NextResponse.json({
|
|
||||||
message: "SAML request completed successfully",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Failed to complete SAML request" },
|
|
||||||
{ status: response.status },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(
|
||||||
"could not create response, redirect user to choose other account",
|
"could not create response, redirect user to choose other account",
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
|
import { Loader2Icon } from "lucide-react";
|
||||||
import { ButtonHTMLAttributes, DetailedHTMLProps, forwardRef } from "react";
|
import { ButtonHTMLAttributes, DetailedHTMLProps, forwardRef } from "react";
|
||||||
|
import { useFormStatus } from "react-dom";
|
||||||
|
|
||||||
export type SignInWithIdentityProviderProps = DetailedHTMLProps<
|
export type SignInWithIdentityProviderProps = DetailedHTMLProps<
|
||||||
ButtonHTMLAttributes<HTMLButtonElement>,
|
ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
@@ -15,15 +17,25 @@ export const BaseButton = forwardRef<
|
|||||||
HTMLButtonElement,
|
HTMLButtonElement,
|
||||||
SignInWithIdentityProviderProps
|
SignInWithIdentityProviderProps
|
||||||
>(function BaseButton(props, ref) {
|
>(function BaseButton(props, ref) {
|
||||||
|
const formStatus = useFormStatus();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
{...props}
|
{...props}
|
||||||
type="button"
|
type="submit"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
disabled={formStatus.pending}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"transition-all cursor-pointer flex flex-row items-center bg-background-light-400 text-text-light-500 dark:bg-background-dark-500 dark:text-text-dark-500 border border-divider-light hover:border-black dark:border-divider-dark hover:dark:border-white focus:border-primary-light-500 focus:dark:border-primary-dark-500 outline-none rounded-md px-4 text-sm",
|
"flex-1 transition-all cursor-pointer flex flex-row items-center bg-background-light-400 text-text-light-500 dark:bg-background-dark-500 dark:text-text-dark-500 border border-divider-light hover:border-black dark:border-divider-dark hover:dark:border-white focus:border-primary-light-500 focus:dark:border-primary-dark-500 outline-none rounded-md px-4 text-sm",
|
||||||
props.className,
|
props.className,
|
||||||
)}
|
)}
|
||||||
/>
|
>
|
||||||
|
<div className="flex-1 justify-between flex items-center gap-4">
|
||||||
|
<div className="flex-1 flex flex-row items-center">
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
{formStatus.pending && <Loader2Icon className="w-4 h-4 animate-spin" />}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@@ -4,6 +4,39 @@ import { useTranslations } from "next-intl";
|
|||||||
import { forwardRef } from "react";
|
import { forwardRef } from "react";
|
||||||
import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
|
import { BaseButton, SignInWithIdentityProviderProps } from "./base-button";
|
||||||
|
|
||||||
|
function GitHubLogo() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
className="h-8 w-8 hidden dark:block"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#fafafa"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M512 0C229.12 0 0 229.12 0 512c0 226.56 146.56 417.92 350.08 485.76 25.6 4.48 35.2-10.88 35.2-24.32 0-12.16-.64-52.48-.64-95.36-128.64 23.68-161.92-31.36-172.16-60.16-5.76-14.72-30.72-60.16-52.48-72.32-17.92-9.6-43.52-33.28-.64-33.92 40.32-.64 69.12 37.12 78.72 52.48 46.08 77.44 119.68 55.68 149.12 42.24 4.48-33.28 17.92-55.68 32.64-68.48-113.92-12.8-232.96-56.96-232.96-252.8 0-55.68 19.84-101.76 52.48-137.6-5.12-12.8-23.04-65.28 5.12-135.68 0 0 42.88-13.44 140.8 52.48 40.96-11.52 84.48-17.28 128-17.28 43.52 0 87.04 5.76 128 17.28 97.92-66.56 140.8-52.48 140.8-52.48 28.16 70.4 10.24 122.88 5.12 135.68 32.64 35.84 52.48 81.28 52.48 137.6 0 196.48-119.68 240-233.6 252.8 18.56 16 34.56 46.72 34.56 94.72 0 68.48-.64 123.52-.64 140.8 0 13.44 9.6 29.44 35.2 24.32C877.44 929.92 1024 737.92 1024 512 1024 229.12 794.88 0 512 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
className="h-8 w-8 block dark:hidden"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#1B1F23"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M512 0C229.12 0 0 229.12 0 512c0 226.56 146.56 417.92 350.08 485.76 25.6 4.48 35.2-10.88 35.2-24.32 0-12.16-.64-52.48-.64-95.36-128.64 23.68-161.92-31.36-172.16-60.16-5.76-14.72-30.72-60.16-52.48-72.32-17.92-9.6-43.52-33.28-.64-33.92 40.32-.64 69.12 37.12 78.72 52.48 46.08 77.44 119.68 55.68 149.12 42.24 4.48-33.28 17.92-55.68 32.64-68.48-113.92-12.8-232.96-56.96-232.96-252.8 0-55.68 19.84-101.76 52.48-137.6-5.12-12.8-23.04-65.28 5.12-135.68 0 0 42.88-13.44 140.8 52.48 40.96-11.52 84.48-17.28 128-17.28 43.52 0 87.04 5.76 128 17.28 97.92-66.56 140.8-52.48 140.8-52.48 28.16 70.4 10.24 122.88 5.12 135.68 32.64 35.84 52.48 81.28 52.48 137.6 0 196.48-119.68 240-233.6 252.8 18.56 16 34.56 46.72 34.56 94.72 0 68.48-.64 123.52-.64 140.8 0 13.44 9.6 29.44 35.2 24.32C877.44 929.92 1024 737.92 1024 512 1024 229.12 794.88 0 512 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export const SignInWithGithub = forwardRef<
|
export const SignInWithGithub = forwardRef<
|
||||||
HTMLButtonElement,
|
HTMLButtonElement,
|
||||||
SignInWithIdentityProviderProps
|
SignInWithIdentityProviderProps
|
||||||
@@ -14,32 +47,7 @@ export const SignInWithGithub = forwardRef<
|
|||||||
return (
|
return (
|
||||||
<BaseButton {...restProps} ref={ref}>
|
<BaseButton {...restProps} ref={ref}>
|
||||||
<div className="mx-2 my-2 flex items-center justify-center">
|
<div className="mx-2 my-2 flex items-center justify-center">
|
||||||
<svg
|
<GitHubLogo />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 1024 1024"
|
|
||||||
className="h-8 w-8 hidden dark:block"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="#fafafa"
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M512 0C229.12 0 0 229.12 0 512c0 226.56 146.56 417.92 350.08 485.76 25.6 4.48 35.2-10.88 35.2-24.32 0-12.16-.64-52.48-.64-95.36-128.64 23.68-161.92-31.36-172.16-60.16-5.76-14.72-30.72-60.16-52.48-72.32-17.92-9.6-43.52-33.28-.64-33.92 40.32-.64 69.12 37.12 78.72 52.48 46.08 77.44 119.68 55.68 149.12 42.24 4.48-33.28 17.92-55.68 32.64-68.48-113.92-12.8-232.96-56.96-232.96-252.8 0-55.68 19.84-101.76 52.48-137.6-5.12-12.8-23.04-65.28 5.12-135.68 0 0 42.88-13.44 140.8 52.48 40.96-11.52 84.48-17.28 128-17.28 43.52 0 87.04 5.76 128 17.28 97.92-66.56 140.8-52.48 140.8-52.48 28.16 70.4 10.24 122.88 5.12 135.68 32.64 35.84 52.48 81.28 52.48 137.6 0 196.48-119.68 240-233.6 252.8 18.56 16 34.56 46.72 34.56 94.72 0 68.48-.64 123.52-.64 140.8 0 13.44 9.6 29.44 35.2 24.32C877.44 929.92 1024 737.92 1024 512 1024 229.12 794.88 0 512 0z"
|
|
||||||
clipRule="evenodd"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 1024 1024"
|
|
||||||
className="h-8 w-8 block dark:hidden"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="#1B1F23"
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M512 0C229.12 0 0 229.12 0 512c0 226.56 146.56 417.92 350.08 485.76 25.6 4.48 35.2-10.88 35.2-24.32 0-12.16-.64-52.48-.64-95.36-128.64 23.68-161.92-31.36-172.16-60.16-5.76-14.72-30.72-60.16-52.48-72.32-17.92-9.6-43.52-33.28-.64-33.92 40.32-.64 69.12 37.12 78.72 52.48 46.08 77.44 119.68 55.68 149.12 42.24 4.48-33.28 17.92-55.68 32.64-68.48-113.92-12.8-232.96-56.96-232.96-252.8 0-55.68 19.84-101.76 52.48-137.6-5.12-12.8-23.04-65.28 5.12-135.68 0 0 42.88-13.44 140.8 52.48 40.96-11.52 84.48-17.28 128-17.28 43.52 0 87.04 5.76 128 17.28 97.92-66.56 140.8-52.48 140.8-52.48 28.16 70.4 10.24 122.88 5.12 135.68 32.64 35.84 52.48 81.28 52.48 137.6 0 196.48-119.68 240-233.6 252.8 18.56 16 34.56 46.72 34.56 94.72 0 68.48-.64 123.52-.64 140.8 0 13.44 9.6 29.44 35.2 24.32C877.44 929.92 1024 737.92 1024 512 1024 229.12 794.88 0 512 0z"
|
|
||||||
clipRule="evenodd"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
{children ? (
|
{children ? (
|
||||||
children
|
children
|
||||||
|
@@ -6,6 +6,7 @@ import { XCircleIcon } from "@heroicons/react/24/outline";
|
|||||||
import { Timestamp, timestampDate } from "@zitadel/client";
|
import { Timestamp, timestampDate } from "@zitadel/client";
|
||||||
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import { useLocale } from "next-intl";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Avatar } from "./avatar";
|
import { Avatar } from "./avatar";
|
||||||
@@ -37,6 +38,9 @@ export function SessionItem({
|
|||||||
reload: () => void;
|
reload: () => void;
|
||||||
requestId?: string;
|
requestId?: string;
|
||||||
}) {
|
}) {
|
||||||
|
const currentLocale = useLocale();
|
||||||
|
moment.locale(currentLocale === "zh" ? "zh-cn" : currentLocale);
|
||||||
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
async function clearSession(id: string) {
|
async function clearSession(id: string) {
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { idpTypeToSlug } from "@/lib/idp";
|
import { idpTypeToSlug } from "@/lib/idp";
|
||||||
import { startIDPFlow } from "@/lib/server/idp";
|
import { redirectToIdp } from "@/lib/server/idp";
|
||||||
import {
|
import {
|
||||||
IdentityProvider,
|
IdentityProvider,
|
||||||
IdentityProviderType,
|
IdentityProviderType,
|
||||||
} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
import { useRouter } from "next/navigation";
|
import { ReactNode, useActionState } from "react";
|
||||||
import { ReactNode, useCallback, useState } from "react";
|
|
||||||
import { Alert } from "./alert";
|
import { Alert } from "./alert";
|
||||||
import { SignInWithIdentityProviderProps } from "./idps/base-button";
|
import { SignInWithIdentityProviderProps } from "./idps/base-button";
|
||||||
import { SignInWithApple } from "./idps/sign-in-with-apple";
|
import { SignInWithApple } from "./idps/sign-in-with-apple";
|
||||||
@@ -31,45 +30,10 @@ export function SignInWithIdp({
|
|||||||
organization,
|
organization,
|
||||||
linkOnly,
|
linkOnly,
|
||||||
}: Readonly<SignInWithIDPProps>) {
|
}: Readonly<SignInWithIDPProps>) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [state, action, _isPending] = useActionState(redirectToIdp, {});
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const startFlow = useCallback(
|
const renderIDPButton = (idp: IdentityProvider, index: number) => {
|
||||||
async (idpId: string, provider: string) => {
|
|
||||||
setLoading(true);
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
if (linkOnly) params.set("link", "true");
|
|
||||||
if (requestId) params.set("requestId", requestId);
|
|
||||||
if (organization) params.set("organization", organization);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await startIDPFlow({
|
|
||||||
idpId,
|
|
||||||
successUrl: `/idp/${provider}/success?` + params.toString(),
|
|
||||||
failureUrl: `/idp/${provider}/failure?` + params.toString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response && "error" in response && response?.error) {
|
|
||||||
setError(response.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response && "redirect" in response && response?.redirect) {
|
|
||||||
return router.push(response.redirect);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
setError("Could not start IDP flow");
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[requestId, organization, linkOnly, router],
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderIDPButton = (idp: IdentityProvider) => {
|
|
||||||
const { id, name, type } = idp;
|
const { id, name, type } = idp;
|
||||||
const onClick = () => startFlow(id, idpTypeToSlug(type));
|
|
||||||
|
|
||||||
const components: Partial<
|
const components: Partial<
|
||||||
Record<
|
Record<
|
||||||
@@ -88,20 +52,32 @@ export function SignInWithIdp({
|
|||||||
),
|
),
|
||||||
[IdentityProviderType.GITLAB]: SignInWithGitlab,
|
[IdentityProviderType.GITLAB]: SignInWithGitlab,
|
||||||
[IdentityProviderType.GITLAB_SELF_HOSTED]: SignInWithGitlab,
|
[IdentityProviderType.GITLAB_SELF_HOSTED]: SignInWithGitlab,
|
||||||
|
[IdentityProviderType.SAML]: SignInWithGeneric,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Component = components[type];
|
const Component = components[type];
|
||||||
return Component ? (
|
return Component ? (
|
||||||
<Component key={id} name={name} onClick={onClick} />
|
<form action={action} className="flex" key={`idp-${index}`}>
|
||||||
|
<input type="hidden" name="id" value={id} />
|
||||||
|
<input type="hidden" name="provider" value={idpTypeToSlug(type)} />
|
||||||
|
<input type="hidden" name="requestId" value={requestId} />
|
||||||
|
<input type="hidden" name="organization" value={organization} />
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="linkOnly"
|
||||||
|
value={linkOnly ? "true" : "false"}
|
||||||
|
/>
|
||||||
|
<Component key={id} name={name} />
|
||||||
|
</form>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-full space-y-2 text-sm">
|
<div className="flex flex-col w-full space-y-2 text-sm">
|
||||||
{identityProviders?.map(renderIDPButton)}
|
{identityProviders?.map(renderIDPButton)}
|
||||||
{error && (
|
{state?.error && (
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<Alert>{error}</Alert>
|
<Alert>{state?.error}</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -20,10 +20,18 @@ export const LANGS: Lang[] = [
|
|||||||
name: "Español",
|
name: "Español",
|
||||||
code: "es",
|
code: "es",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Polski",
|
||||||
|
code: "pl",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "简体中文",
|
name: "简体中文",
|
||||||
code: "zh",
|
code: "zh",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Русский",
|
||||||
|
code: "ru",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const LANGUAGE_COOKIE_NAME = "NEXT_LOCALE";
|
export const LANGUAGE_COOKIE_NAME = "NEXT_LOCALE";
|
||||||
|
@@ -1,11 +1,5 @@
|
|||||||
import { create } from "@zitadel/client";
|
|
||||||
import { IDPType } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
|
import { IDPType } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
|
||||||
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
import { IDPInformation } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
|
||||||
import {
|
|
||||||
AddHumanUserRequest,
|
|
||||||
AddHumanUserRequestSchema,
|
|
||||||
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
|
||||||
|
|
||||||
// This maps the IdentityProviderType to a slug which is used in the /success and /failure routes
|
// This maps the IdentityProviderType to a slug which is used in the /success and /failure routes
|
||||||
export function idpTypeToSlug(idpType: IdentityProviderType) {
|
export function idpTypeToSlug(idpType: IdentityProviderType) {
|
||||||
@@ -74,189 +68,3 @@ export function idpTypeToIdentityProviderType(
|
|||||||
throw new Error("Unknown identity provider type");
|
throw new Error("Unknown identity provider type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this maps the IDPInformation to the AddHumanUserRequest which is used when creating a user or linking a user (email)
|
|
||||||
// TODO: extend this object from a other file which can be overwritten by customers like map = { ...PROVIDER_MAPPING, ...customerMap }
|
|
||||||
export type OIDC_USER = {
|
|
||||||
User: {
|
|
||||||
email: string;
|
|
||||||
name?: string;
|
|
||||||
given_name?: string;
|
|
||||||
family_name?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const GITLAB_MAPPING = (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation as {
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
email_verified: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
return create(AddHumanUserRequestSchema, {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo.email,
|
|
||||||
verification: { case: "isVerified", value: rawInfo.email_verified },
|
|
||||||
},
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo.name || idp.userName || "",
|
|
||||||
givenName: "",
|
|
||||||
familyName: "",
|
|
||||||
},
|
|
||||||
idpLinks: [
|
|
||||||
{
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const OIDC_MAPPING = (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation as OIDC_USER;
|
|
||||||
|
|
||||||
return create(AddHumanUserRequestSchema, {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo.User?.email,
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo.User?.name ?? "",
|
|
||||||
givenName: rawInfo.User?.given_name ?? "",
|
|
||||||
familyName: rawInfo.User?.family_name ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [
|
|
||||||
{
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const GITHUB_MAPPING = (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation as {
|
|
||||||
email: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
return create(AddHumanUserRequestSchema, {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo.email,
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo.name ?? "",
|
|
||||||
givenName: rawInfo.name ?? "",
|
|
||||||
familyName: rawInfo.name ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [
|
|
||||||
{
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PROVIDER_MAPPING: {
|
|
||||||
[provider: number]: (rI: IDPInformation) => AddHumanUserRequest;
|
|
||||||
} = {
|
|
||||||
[IdentityProviderType.GOOGLE]: (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation as OIDC_USER;
|
|
||||||
|
|
||||||
return create(AddHumanUserRequestSchema, {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo.User?.email,
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo.User?.name ?? "",
|
|
||||||
givenName: rawInfo.User?.given_name ?? "",
|
|
||||||
familyName: rawInfo.User?.family_name ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [
|
|
||||||
{
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[IdentityProviderType.GITLAB]: GITLAB_MAPPING,
|
|
||||||
[IdentityProviderType.GITLAB_SELF_HOSTED]: GITLAB_MAPPING,
|
|
||||||
[IdentityProviderType.OIDC]: OIDC_MAPPING,
|
|
||||||
// check
|
|
||||||
[IdentityProviderType.OAUTH]: OIDC_MAPPING,
|
|
||||||
[IdentityProviderType.AZURE_AD]: (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation as {
|
|
||||||
jobTitle: string;
|
|
||||||
mail: string;
|
|
||||||
mobilePhone: string;
|
|
||||||
preferredLanguage: string;
|
|
||||||
id: string;
|
|
||||||
displayName?: string;
|
|
||||||
givenName?: string;
|
|
||||||
surname?: string;
|
|
||||||
officeLocation?: string;
|
|
||||||
userPrincipalName: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
return create(AddHumanUserRequestSchema, {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo.mail || rawInfo.userPrincipalName || "",
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo.displayName ?? "",
|
|
||||||
givenName: rawInfo.givenName ?? "",
|
|
||||||
familyName: rawInfo.surname ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [
|
|
||||||
{
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[IdentityProviderType.GITHUB]: GITHUB_MAPPING,
|
|
||||||
[IdentityProviderType.GITHUB_ES]: GITHUB_MAPPING,
|
|
||||||
[IdentityProviderType.APPLE]: (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation as {
|
|
||||||
name?: string;
|
|
||||||
firstName?: string;
|
|
||||||
lastName?: string;
|
|
||||||
email?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
return create(AddHumanUserRequestSchema, {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo.email ?? "",
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo.name ?? "",
|
|
||||||
givenName: rawInfo.firstName ?? "",
|
|
||||||
familyName: rawInfo.lastName ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [
|
|
||||||
{
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
@@ -6,11 +6,45 @@ import {
|
|||||||
startIdentityProviderFlow,
|
startIdentityProviderFlow,
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
import { getNextUrl } from "../client";
|
import { getNextUrl } from "../client";
|
||||||
import { getServiceUrlFromHeaders } from "../service";
|
import { getServiceUrlFromHeaders } from "../service";
|
||||||
import { checkEmailVerification } from "../verify-helper";
|
import { checkEmailVerification } from "../verify-helper";
|
||||||
import { createSessionForIdpAndUpdateCookie } from "./cookie";
|
import { createSessionForIdpAndUpdateCookie } from "./cookie";
|
||||||
|
|
||||||
|
export type RedirectToIdpState = { error?: string | null } | undefined;
|
||||||
|
|
||||||
|
export async function redirectToIdp(
|
||||||
|
prevState: RedirectToIdpState,
|
||||||
|
formData: FormData,
|
||||||
|
): Promise<RedirectToIdpState> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
const linkOnly = formData.get("linkOnly") === "true";
|
||||||
|
const requestId = formData.get("requestId") as string;
|
||||||
|
const organization = formData.get("organization") as string;
|
||||||
|
const idpId = formData.get("id") as string;
|
||||||
|
const provider = formData.get("provider") as string;
|
||||||
|
|
||||||
|
if (linkOnly) params.set("link", "true");
|
||||||
|
if (requestId) params.set("requestId", requestId);
|
||||||
|
if (organization) params.set("organization", organization);
|
||||||
|
|
||||||
|
const response = await startIDPFlow({
|
||||||
|
idpId,
|
||||||
|
successUrl: `/idp/${provider}/success?` + params.toString(),
|
||||||
|
failureUrl: `/idp/${provider}/failure?` + params.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response && "error" in response && response?.error) {
|
||||||
|
return { error: response.error };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response && "redirect" in response && response?.redirect) {
|
||||||
|
redirect(response.redirect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type StartIDPFlowCommand = {
|
export type StartIDPFlowCommand = {
|
||||||
idpId: string;
|
idpId: string;
|
||||||
successUrl: string;
|
successUrl: string;
|
||||||
|
@@ -915,27 +915,6 @@ export async function startIdentityProviderFlow({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function retrieveIdentityProviderInformation({
|
|
||||||
serviceUrl,
|
|
||||||
idpIntentId,
|
|
||||||
idpIntentToken,
|
|
||||||
}: {
|
|
||||||
serviceUrl: string;
|
|
||||||
|
|
||||||
idpIntentId: string;
|
|
||||||
idpIntentToken: string;
|
|
||||||
}) {
|
|
||||||
const userService: Client<typeof UserService> = await createServiceForHost(
|
|
||||||
UserService,
|
|
||||||
serviceUrl,
|
|
||||||
);
|
|
||||||
|
|
||||||
return userService.retrieveIdentityProviderIntent({
|
|
||||||
idpIntentId,
|
|
||||||
idpIntentToken,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getAuthRequest({
|
export async function getAuthRequest({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
authRequestId,
|
authRequestId,
|
||||||
|
@@ -22,7 +22,9 @@ export async function middleware(request: NextRequest) {
|
|||||||
|
|
||||||
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
||||||
|
|
||||||
const instanceHost = `${serviceUrl}`.replace("https://", "");
|
const instanceHost = `${serviceUrl}`
|
||||||
|
.replace("https://", "")
|
||||||
|
.replace("http://", "");
|
||||||
|
|
||||||
const requestHeaders = new Headers(request.headers);
|
const requestHeaders = new Headers(request.headers);
|
||||||
|
|
||||||
|
@@ -26,6 +26,10 @@
|
|||||||
"release": "turbo run build --filter=login^... && changeset publish",
|
"release": "turbo run build --filter=login^... && changeset publish",
|
||||||
"run-zitadel": "docker compose -f ./acceptance/docker-compose.yaml run setup",
|
"run-zitadel": "docker compose -f ./acceptance/docker-compose.yaml run setup",
|
||||||
"run-sink": "docker compose -f ./acceptance/docker-compose.yaml up -d sink",
|
"run-sink": "docker compose -f ./acceptance/docker-compose.yaml up -d sink",
|
||||||
|
"run-samlsp": "docker compose -f ./acceptance/saml/docker-compose.yaml up -d",
|
||||||
|
"run-samlidp": "docker compose -f ./acceptance/idp/saml/docker-compose.yaml up -d",
|
||||||
|
"run-oidcrp": "docker compose -f ./acceptance/oidc/docker-compose.yaml up -d",
|
||||||
|
"run-oidcop": "docker compose -f ./acceptance/idp/oidc/docker-compose.yaml up -d",
|
||||||
"stop": "docker compose -f ./acceptance/docker-compose.yaml stop"
|
"stop": "docker compose -f ./acceptance/docker-compose.yaml stop"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
@@ -43,7 +47,7 @@
|
|||||||
"@types/node": "^20.17.17",
|
"@types/node": "^20.17.17",
|
||||||
"@vitejs/plugin-react": "^4.3.3",
|
"@vitejs/plugin-react": "^4.3.3",
|
||||||
"@zitadel/prettier-config": "workspace:*",
|
"@zitadel/prettier-config": "workspace:*",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.8.2",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"@zitadel/eslint-config": "workspace:*",
|
"@zitadel/eslint-config": "workspace:*",
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"generate": "buf generate https://github.com/zitadel/zitadel.git#tag=v2.71.1 --path ./proto/zitadel",
|
"generate": "buf generate https://github.com/zitadel/zitadel.git --path ./proto/zitadel",
|
||||||
"clean": "rm -rf zitadel .turbo node_modules google protoc-gen-openapiv2 validate"
|
"clean": "rm -rf zitadel .turbo node_modules google protoc-gen-openapiv2 validate"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
470
pnpm-lock.yaml
generated
470
pnpm-lock.yaml
generated
@@ -42,8 +42,8 @@ importers:
|
|||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:packages/zitadel-prettier-config
|
version: link:packages/zitadel-prettier-config
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.7.7
|
specifier: ^1.8.2
|
||||||
version: 1.7.7(debug@4.3.7)
|
version: 1.8.3(debug@4.3.7)
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.4.5
|
specifier: ^16.4.5
|
||||||
version: 16.4.5
|
version: 16.4.5
|
||||||
@@ -85,7 +85,7 @@ importers:
|
|||||||
version: 0.5.7(tailwindcss@3.4.14)
|
version: 0.5.7(tailwindcss@3.4.14)
|
||||||
'@vercel/analytics':
|
'@vercel/analytics':
|
||||||
specifier: ^1.2.2
|
specifier: ^1.2.2
|
||||||
version: 1.3.1(next@15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0)
|
version: 1.3.1(next@15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0)
|
||||||
'@zitadel/client':
|
'@zitadel/client':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/zitadel-client
|
version: link:../../packages/zitadel-client
|
||||||
@@ -104,18 +104,21 @@ importers:
|
|||||||
jose:
|
jose:
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.8.0
|
version: 5.8.0
|
||||||
|
lucide-react:
|
||||||
|
specifier: 0.469.0
|
||||||
|
version: 0.469.0(react@19.0.0)
|
||||||
moment:
|
moment:
|
||||||
specifier: ^2.29.4
|
specifier: ^2.29.4
|
||||||
version: 2.30.1
|
version: 2.30.1
|
||||||
next:
|
next:
|
||||||
specifier: 15.2.0-canary.33
|
specifier: 15.3.1-canary.9
|
||||||
version: 15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
version: 15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
||||||
next-intl:
|
next-intl:
|
||||||
specifier: ^3.25.1
|
specifier: ^3.25.1
|
||||||
version: 3.25.1(next@15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0)
|
version: 3.25.1(next@15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0)
|
||||||
next-themes:
|
next-themes:
|
||||||
specifier: ^0.2.1
|
specifier: ^0.2.1
|
||||||
version: 0.2.1(next@15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 0.2.1(next@15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
nice-grpc:
|
nice-grpc:
|
||||||
specifier: 2.0.1
|
specifier: 2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
@@ -405,6 +408,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
|
resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/runtime@7.26.10':
|
||||||
|
resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@babel/template@7.25.9':
|
'@babel/template@7.25.9':
|
||||||
resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
|
resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -600,8 +607,8 @@ packages:
|
|||||||
'@cypress/xvfb@1.2.4':
|
'@cypress/xvfb@1.2.4':
|
||||||
resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
|
resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
|
||||||
|
|
||||||
'@emnapi/runtime@1.3.1':
|
'@emnapi/runtime@1.4.3':
|
||||||
resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==}
|
resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.21.5':
|
'@esbuild/aix-ppc64@0.21.5':
|
||||||
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
|
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
|
||||||
@@ -916,6 +923,7 @@ packages:
|
|||||||
'@faker-js/faker@9.2.0':
|
'@faker-js/faker@9.2.0':
|
||||||
resolution: {integrity: sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==}
|
resolution: {integrity: sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==}
|
||||||
engines: {node: '>=18.0.0', npm: '>=9.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=9.0.0'}
|
||||||
|
deprecated: Please update to a newer version
|
||||||
|
|
||||||
'@floating-ui/core@1.6.8':
|
'@floating-ui/core@1.6.8':
|
||||||
resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
|
resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
|
||||||
@@ -996,107 +1004,112 @@ packages:
|
|||||||
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
|
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
|
||||||
deprecated: Use @eslint/object-schema instead
|
deprecated: Use @eslint/object-schema instead
|
||||||
|
|
||||||
'@img/sharp-darwin-arm64@0.33.5':
|
'@img/sharp-darwin-arm64@0.34.1':
|
||||||
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
|
resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-darwin-x64@0.33.5':
|
'@img/sharp-darwin-x64@0.34.1':
|
||||||
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
|
resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
'@img/sharp-libvips-darwin-arm64@1.1.0':
|
||||||
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
|
resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
'@img/sharp-libvips-darwin-x64@1.1.0':
|
||||||
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
|
resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
'@img/sharp-libvips-linux-arm64@1.1.0':
|
||||||
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
'@img/sharp-libvips-linux-arm@1.1.0':
|
||||||
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
'@img/sharp-libvips-linux-ppc64@1.1.0':
|
||||||
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
|
resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-s390x@1.1.0':
|
||||||
|
resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
'@img/sharp-libvips-linux-x64@1.1.0':
|
||||||
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
'@img/sharp-libvips-linuxmusl-arm64@1.1.0':
|
||||||
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
|
resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
'@img/sharp-libvips-linuxmusl-x64@1.1.0':
|
||||||
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
|
resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-arm64@0.33.5':
|
'@img/sharp-linux-arm64@0.34.1':
|
||||||
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-arm@0.33.5':
|
'@img/sharp-linux-arm@0.34.1':
|
||||||
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-s390x@0.33.5':
|
'@img/sharp-linux-s390x@0.34.1':
|
||||||
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
|
resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-x64@0.33.5':
|
'@img/sharp-linux-x64@0.34.1':
|
||||||
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
'@img/sharp-linuxmusl-arm64@0.34.1':
|
||||||
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
|
resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
'@img/sharp-linuxmusl-x64@0.34.1':
|
||||||
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
|
resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-wasm32@0.33.5':
|
'@img/sharp-wasm32@0.34.1':
|
||||||
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
|
resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [wasm32]
|
cpu: [wasm32]
|
||||||
|
|
||||||
'@img/sharp-win32-ia32@0.33.5':
|
'@img/sharp-win32-ia32@0.34.1':
|
||||||
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
|
resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@img/sharp-win32-x64@0.33.5':
|
'@img/sharp-win32-x64@0.34.1':
|
||||||
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
|
resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
@@ -1136,56 +1149,56 @@ packages:
|
|||||||
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
|
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@next/env@15.2.0-canary.33':
|
'@next/env@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-y3EPM+JYKU8t2K+i6bc0QrotEZVGpqu9eVjprj4cfS8QZyZcL54s+W9aGB0TBuGavU9tQdZ50W186+toeMV+hw==}
|
resolution: {integrity: sha512-ahnXk9D1SECEeq6KhZBAkhuijmvONJqXoAmcmyVcVekq+u5I7LAQe8A7AFTSU0d5jItwJ+bfnpA6ZDGaXVG2CQ==}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@14.2.18':
|
'@next/eslint-plugin-next@14.2.18':
|
||||||
resolution: {integrity: sha512-KyYTbZ3GQwWOjX3Vi1YcQbekyGP0gdammb7pbmmi25HBUCINzDReyrzCMOJIeZisK1Q3U6DT5Rlc4nm2/pQeXA==}
|
resolution: {integrity: sha512-KyYTbZ3GQwWOjX3Vi1YcQbekyGP0gdammb7pbmmi25HBUCINzDReyrzCMOJIeZisK1Q3U6DT5Rlc4nm2/pQeXA==}
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.2.0-canary.33':
|
'@next/swc-darwin-arm64@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-+fCdK2KmR6lWoCTk1fSd5pvbiLZHfZF+D/Xdz3xrXw+pbnBtXWLKQrPT0bCtDseMxD31qcOywq5mAApvI3EGpA==}
|
resolution: {integrity: sha512-/44Wi2KxNg1pz/Q+jNs+m0ze0Lzp7Ian1lFO22B2UcAdgPaThBp/ItBro5G39Oge6n0O0xpp2+HS3YbydJ5lOg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.2.0-canary.33':
|
'@next/swc-darwin-x64@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-GrrU+tSmeBRow+7bnn7i5M96g3tc28hPH5t5Y65qUXGmmrZwGZN1e1d+8QbXPdAGkvjEPcOkUNQuQVpp1qpYPA==}
|
resolution: {integrity: sha512-jl7BJS/lysYlUa7rYPYKM6udKuXEUoI3e31g+8wMFbLmevivnnowzPc7yCJ3MAQmW0J6jv9U9obo+a2n5wafsQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.2.0-canary.33':
|
'@next/swc-linux-arm64-gnu@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-8RnGxnUpASHoUf6aHUifmZom5b4Ow5nTdCib/CNYXZ6VLuL5ocvmr+DXs/SKzi9h8OHR7JkLwKXHCcF8WyscSg==}
|
resolution: {integrity: sha512-7Lj6VFzrkO82ojfTBlxIyCMKylgpCpCflekU5sJgD0ooRGcWlWSEVCiUcOSvMPeHW7TiJqTTB2NVzuHhY+yLhA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.2.0-canary.33':
|
'@next/swc-linux-arm64-musl@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-COyE0LzMuLBZSR+Z/TOGilyJPdwSU588Vt0+o8GoECkoDEnjyuO2s2nHa2kDAcEfUEPkhlo0tErU3mF+8AVOTQ==}
|
resolution: {integrity: sha512-iqZSNVVD+PDASb64m/WD0nCRNXXd3eLIwC/U1YjfYNWwW2I0OJL1bLX0kTp57wQadYMqyv52a96yEaeen+47AQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.2.0-canary.33':
|
'@next/swc-linux-x64-gnu@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-3Y9lqJs+ftU9jgbLdCtvAvF8MNJsJYGMH7icb8QMs1+yOyHHbmwkZoElKdjwfUWzQ2sX28ywp73GWq4HbrsoUg==}
|
resolution: {integrity: sha512-CLlCDftzkvzx+kqrp6J/lY+K7x9cNFlAwpCUJZxrzX+m5TDwgMLD045z4+Jwj+3gLF3Q+6ubPSZly0q5EwpfRQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.2.0-canary.33':
|
'@next/swc-linux-x64-musl@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-FS9iA+RkZlhdWGQEKtsplVBXIYZJUn5nsRB+1UY46b3uaL6dDypu13ODaSwYuAwXGgkrZBVF9AFO3y4biBnPlA==}
|
resolution: {integrity: sha512-9CL3IMmfabYNHjr3Z9hw0jKVe/HBd/WhOmWvF4xtbtVHZuQcz74O2O77BPNHe8NEuhG11WiFhJsnbkSbkFJ+sQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.2.0-canary.33':
|
'@next/swc-win32-arm64-msvc@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-Ji9CtBbUx06qvvN/rPohJN2FEFGsUv26F50f2nMRYRwrq3POXDjloGOiRocrjU0ty/cUzCz71qTUfKdmv/ajmg==}
|
resolution: {integrity: sha512-sITL9Fru9NB00sXC/+gDKpxojAPlVYIwzI1bKzugSgLwnDyydbSnqMgmea1NOr/7nFbeBnWDXpYHgq8T4VX2Bg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.2.0-canary.33':
|
'@next/swc-win32-x64-msvc@15.3.1-canary.9':
|
||||||
resolution: {integrity: sha512-hjdbGnkwIZ8zN2vlS6lNsEJO37HRtcEGimzfkruBMsi/DwJBqkJvZbNC/XCJy3HFcU58igncqV52p1IPjmAJAw==}
|
resolution: {integrity: sha512-ickRm1FKSMHaELoN//K1CMEMSTJpclg4KgaAqmv0fDnOvBLsCw/reAoZrT9N+Bt2ToDP4jbwEEhSOmH8uQ1lQg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
@@ -1479,9 +1492,6 @@ packages:
|
|||||||
'@swc/counter@0.1.3':
|
'@swc/counter@0.1.3':
|
||||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||||
|
|
||||||
'@swc/helpers@0.5.13':
|
|
||||||
resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==}
|
|
||||||
|
|
||||||
'@swc/helpers@0.5.15':
|
'@swc/helpers@0.5.15':
|
||||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||||
|
|
||||||
@@ -1899,8 +1909,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
|
resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
axios@1.7.7:
|
axios@1.8.3:
|
||||||
resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==}
|
resolution: {integrity: sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==}
|
||||||
|
|
||||||
axobject-query@3.1.1:
|
axobject-query@3.1.1:
|
||||||
resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==}
|
resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==}
|
||||||
@@ -1972,6 +1982,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==}
|
resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
call-bind-apply-helpers@1.0.2:
|
||||||
|
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
call-bind@1.0.7:
|
call-bind@1.0.7:
|
||||||
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2316,6 +2330,10 @@ packages:
|
|||||||
dprint-node@1.0.8:
|
dprint-node@1.0.8:
|
||||||
resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==}
|
resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==}
|
||||||
|
|
||||||
|
dunder-proto@1.0.1:
|
||||||
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
duplexer@0.1.2:
|
duplexer@0.1.2:
|
||||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||||
|
|
||||||
@@ -2372,6 +2390,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
|
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-define-property@1.0.1:
|
||||||
|
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
es-errors@1.3.0:
|
es-errors@1.3.0:
|
||||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2387,10 +2409,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
|
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-object-atoms@1.1.1:
|
||||||
|
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
es-set-tostringtag@2.0.3:
|
es-set-tostringtag@2.0.3:
|
||||||
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
|
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-set-tostringtag@2.1.0:
|
||||||
|
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
es-shim-unscopables@1.0.2:
|
es-shim-unscopables@1.0.2:
|
||||||
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
|
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
|
||||||
|
|
||||||
@@ -2673,8 +2703,8 @@ packages:
|
|||||||
flatted@3.3.1:
|
flatted@3.3.1:
|
||||||
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
|
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
|
||||||
|
|
||||||
follow-redirects@1.15.6:
|
follow-redirects@1.15.9:
|
||||||
resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
|
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
debug: '*'
|
debug: '*'
|
||||||
@@ -2696,6 +2726,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
form-data@4.0.2:
|
||||||
|
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
fraction.js@4.3.7:
|
fraction.js@4.3.7:
|
||||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||||
|
|
||||||
@@ -2762,6 +2796,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
|
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
get-intrinsic@1.3.0:
|
||||||
|
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
get-proto@1.0.1:
|
||||||
|
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
get-stream@5.2.0:
|
get-stream@5.2.0:
|
||||||
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
|
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2838,6 +2880,10 @@ packages:
|
|||||||
gopd@1.0.1:
|
gopd@1.0.1:
|
||||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
||||||
|
|
||||||
|
gopd@1.2.0:
|
||||||
|
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
graceful-fs@4.2.11:
|
graceful-fs@4.2.11:
|
||||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||||
|
|
||||||
@@ -2870,6 +2916,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
has-symbols@1.1.0:
|
||||||
|
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
has-tostringtag@1.0.2:
|
has-tostringtag@1.0.2:
|
||||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3339,6 +3389,11 @@ packages:
|
|||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||||
|
|
||||||
|
lucide-react@0.469.0:
|
||||||
|
resolution: {integrity: sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
lz-string@1.5.0:
|
lz-string@1.5.0:
|
||||||
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
|
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3362,6 +3417,10 @@ packages:
|
|||||||
map-stream@0.1.0:
|
map-stream@0.1.0:
|
||||||
resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
|
resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
|
||||||
|
|
||||||
|
math-intrinsics@1.1.0:
|
||||||
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
meow@13.2.0:
|
meow@13.2.0:
|
||||||
resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
|
resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -3474,8 +3533,8 @@ packages:
|
|||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
|
|
||||||
next@15.2.0-canary.33:
|
next@15.3.1-canary.9:
|
||||||
resolution: {integrity: sha512-WF8QLeYkakuYwksdWY/F+Bi8tNJfIbiSYk9hCmldn9sNp1lU3lqI1hrW1ynbcMSaXC+qQEr7yol2OdvVZ4nZYQ==}
|
resolution: {integrity: sha512-aCGJOPF7+e3uElbeOLc5BMhlhQ0Q0J9EAOZv47lkm/VsRgjVWh98yYy4KZg7H11YONQX7zEUa2nuD4IVbG/1KQ==}
|
||||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -4110,6 +4169,11 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
semver@7.7.1:
|
||||||
|
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
server-only@0.0.1:
|
server-only@0.0.1:
|
||||||
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
|
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
|
||||||
|
|
||||||
@@ -4124,8 +4188,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
|
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
sharp@0.33.5:
|
sharp@0.34.1:
|
||||||
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
|
resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
|
||||||
shebang-command@1.2.0:
|
shebang-command@1.2.0:
|
||||||
@@ -4980,6 +5044,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime: 0.14.1
|
regenerator-runtime: 0.14.1
|
||||||
|
|
||||||
|
'@babel/runtime@7.26.10':
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime: 0.14.1
|
||||||
|
|
||||||
'@babel/template@7.25.9':
|
'@babel/template@7.25.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.26.2
|
'@babel/code-frame': 7.26.2
|
||||||
@@ -5258,7 +5326,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@emnapi/runtime@1.3.1':
|
'@emnapi/runtime@1.4.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
optional: true
|
optional: true
|
||||||
@@ -5533,79 +5601,82 @@ snapshots:
|
|||||||
|
|
||||||
'@humanwhocodes/object-schema@2.0.3': {}
|
'@humanwhocodes/object-schema@2.0.3': {}
|
||||||
|
|
||||||
'@img/sharp-darwin-arm64@0.33.5':
|
'@img/sharp-darwin-arm64@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
'@img/sharp-libvips-darwin-arm64': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-darwin-x64@0.33.5':
|
'@img/sharp-darwin-x64@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-darwin-x64': 1.0.4
|
'@img/sharp-libvips-darwin-x64': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
'@img/sharp-libvips-darwin-arm64@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
'@img/sharp-libvips-darwin-x64@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
'@img/sharp-libvips-linux-arm64@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
'@img/sharp-libvips-linux-arm@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
'@img/sharp-libvips-linux-ppc64@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
'@img/sharp-libvips-linux-s390x@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
'@img/sharp-libvips-linux-x64@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
'@img/sharp-libvips-linuxmusl-arm64@1.1.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-arm64@0.33.5':
|
'@img/sharp-libvips-linuxmusl-x64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-arm64': 1.0.4
|
'@img/sharp-libvips-linux-arm64': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-arm@0.33.5':
|
'@img/sharp-linux-arm@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-arm': 1.0.5
|
'@img/sharp-libvips-linux-arm': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-s390x@0.33.5':
|
'@img/sharp-linux-s390x@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-s390x': 1.0.4
|
'@img/sharp-libvips-linux-s390x': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-x64@0.33.5':
|
'@img/sharp-linux-x64@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-x64': 1.0.4
|
'@img/sharp-libvips-linux-x64': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
'@img/sharp-linuxmusl-arm64@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
|
'@img/sharp-libvips-linuxmusl-arm64': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
'@img/sharp-linuxmusl-x64@0.34.1':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
|
'@img/sharp-libvips-linuxmusl-x64': 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-wasm32@0.33.5':
|
'@img/sharp-wasm32@0.34.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emnapi/runtime': 1.3.1
|
'@emnapi/runtime': 1.4.3
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-win32-ia32@0.33.5':
|
'@img/sharp-win32-ia32@0.34.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-win32-x64@0.33.5':
|
'@img/sharp-win32-x64@0.34.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@isaacs/cliui@8.0.2':
|
'@isaacs/cliui@8.0.2':
|
||||||
@@ -5667,34 +5738,34 @@ snapshots:
|
|||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@next/env@15.2.0-canary.33': {}
|
'@next/env@15.3.1-canary.9': {}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@14.2.18':
|
'@next/eslint-plugin-next@14.2.18':
|
||||||
dependencies:
|
dependencies:
|
||||||
glob: 10.3.10
|
glob: 10.3.10
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.2.0-canary.33':
|
'@next/swc-darwin-arm64@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.2.0-canary.33':
|
'@next/swc-darwin-x64@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.2.0-canary.33':
|
'@next/swc-linux-arm64-gnu@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.2.0-canary.33':
|
'@next/swc-linux-arm64-musl@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.2.0-canary.33':
|
'@next/swc-linux-x64-gnu@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.2.0-canary.33':
|
'@next/swc-linux-x64-musl@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.2.0-canary.33':
|
'@next/swc-win32-arm64-msvc@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.2.0-canary.33':
|
'@next/swc-win32-x64-msvc@15.3.1-canary.9':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
|
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
|
||||||
@@ -5836,7 +5907,7 @@ snapshots:
|
|||||||
|
|
||||||
'@react-aria/ssr@3.9.6(react@19.0.0)':
|
'@react-aria/ssr@3.9.6(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@swc/helpers': 0.5.5
|
'@swc/helpers': 0.5.15
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|
||||||
'@react-aria/utils@3.25.3(react@19.0.0)':
|
'@react-aria/utils@3.25.3(react@19.0.0)':
|
||||||
@@ -5844,13 +5915,13 @@ snapshots:
|
|||||||
'@react-aria/ssr': 3.9.6(react@19.0.0)
|
'@react-aria/ssr': 3.9.6(react@19.0.0)
|
||||||
'@react-stately/utils': 3.10.4(react@19.0.0)
|
'@react-stately/utils': 3.10.4(react@19.0.0)
|
||||||
'@react-types/shared': 3.25.0(react@19.0.0)
|
'@react-types/shared': 3.25.0(react@19.0.0)
|
||||||
'@swc/helpers': 0.5.5
|
'@swc/helpers': 0.5.15
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|
||||||
'@react-stately/utils@3.10.4(react@19.0.0)':
|
'@react-stately/utils@3.10.4(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@swc/helpers': 0.5.13
|
'@swc/helpers': 0.5.15
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|
||||||
'@react-types/shared@3.25.0(react@19.0.0)':
|
'@react-types/shared@3.25.0(react@19.0.0)':
|
||||||
@@ -5925,10 +5996,6 @@ snapshots:
|
|||||||
|
|
||||||
'@swc/counter@0.1.3': {}
|
'@swc/counter@0.1.3': {}
|
||||||
|
|
||||||
'@swc/helpers@0.5.13':
|
|
||||||
dependencies:
|
|
||||||
tslib: 2.8.1
|
|
||||||
|
|
||||||
'@swc/helpers@0.5.15':
|
'@swc/helpers@0.5.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
@@ -5959,7 +6026,7 @@ snapshots:
|
|||||||
'@testing-library/dom@10.4.0':
|
'@testing-library/dom@10.4.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.26.2
|
'@babel/code-frame': 7.26.2
|
||||||
'@babel/runtime': 7.26.0
|
'@babel/runtime': 7.26.10
|
||||||
'@types/aria-query': 5.0.4
|
'@types/aria-query': 5.0.4
|
||||||
aria-query: 5.3.0
|
aria-query: 5.3.0
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
@@ -6154,11 +6221,11 @@ snapshots:
|
|||||||
|
|
||||||
'@ungap/structured-clone@1.2.0': {}
|
'@ungap/structured-clone@1.2.0': {}
|
||||||
|
|
||||||
'@vercel/analytics@1.3.1(next@15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0)':
|
'@vercel/analytics@1.3.1(next@15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
server-only: 0.0.1
|
server-only: 0.0.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
next: 15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
next: 15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|
||||||
'@vercel/git-hooks@1.0.0': {}
|
'@vercel/git-hooks@1.0.0': {}
|
||||||
@@ -6407,10 +6474,10 @@ snapshots:
|
|||||||
|
|
||||||
axe-core@4.10.0: {}
|
axe-core@4.10.0: {}
|
||||||
|
|
||||||
axios@1.7.7(debug@4.3.7):
|
axios@1.8.3(debug@4.3.7):
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.6(debug@4.3.7)
|
follow-redirects: 1.15.9(debug@4.3.7)
|
||||||
form-data: 4.0.0
|
form-data: 4.0.2
|
||||||
proxy-from-env: 1.1.0
|
proxy-from-env: 1.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
@@ -6484,12 +6551,17 @@ snapshots:
|
|||||||
|
|
||||||
cachedir@2.4.0: {}
|
cachedir@2.4.0: {}
|
||||||
|
|
||||||
|
call-bind-apply-helpers@1.0.2:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
function-bind: 1.1.2
|
||||||
|
|
||||||
call-bind@1.0.7:
|
call-bind@1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-define-property: 1.0.0
|
es-define-property: 1.0.0
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
set-function-length: 1.2.2
|
set-function-length: 1.2.2
|
||||||
|
|
||||||
callsites@3.1.0: {}
|
callsites@3.1.0: {}
|
||||||
@@ -6866,6 +6938,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
detect-libc: 1.0.3
|
detect-libc: 1.0.3
|
||||||
|
|
||||||
|
dunder-proto@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
es-errors: 1.3.0
|
||||||
|
gopd: 1.2.0
|
||||||
|
|
||||||
duplexer@0.1.2: {}
|
duplexer@0.1.2: {}
|
||||||
|
|
||||||
eastasianwidth@0.2.0: {}
|
eastasianwidth@0.2.0: {}
|
||||||
@@ -6961,13 +7039,15 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.2.4
|
||||||
|
|
||||||
|
es-define-property@1.0.1: {}
|
||||||
|
|
||||||
es-errors@1.3.0: {}
|
es-errors@1.3.0: {}
|
||||||
|
|
||||||
es-get-iterator@1.1.3:
|
es-get-iterator@1.1.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.1.0
|
||||||
is-arguments: 1.1.1
|
is-arguments: 1.1.1
|
||||||
is-map: 2.0.3
|
is-map: 2.0.3
|
||||||
is-set: 2.0.3
|
is-set: 2.0.3
|
||||||
@@ -6996,12 +7076,23 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
|
|
||||||
|
es-object-atoms@1.1.1:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
|
||||||
es-set-tostringtag@2.0.3:
|
es-set-tostringtag@2.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.2.4
|
||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
|
|
||||||
|
es-set-tostringtag@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
get-intrinsic: 1.3.0
|
||||||
|
has-tostringtag: 1.0.2
|
||||||
|
hasown: 2.0.2
|
||||||
|
|
||||||
es-shim-unscopables@1.0.2:
|
es-shim-unscopables@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
@@ -7437,7 +7528,7 @@ snapshots:
|
|||||||
|
|
||||||
flatted@3.3.1: {}
|
flatted@3.3.1: {}
|
||||||
|
|
||||||
follow-redirects@1.15.6(debug@4.3.7):
|
follow-redirects@1.15.9(debug@4.3.7):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
debug: 4.3.7(supports-color@5.5.0)
|
debug: 4.3.7(supports-color@5.5.0)
|
||||||
|
|
||||||
@@ -7458,6 +7549,13 @@ snapshots:
|
|||||||
combined-stream: 1.0.8
|
combined-stream: 1.0.8
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
|
form-data@4.0.2:
|
||||||
|
dependencies:
|
||||||
|
asynckit: 0.4.0
|
||||||
|
combined-stream: 1.0.8
|
||||||
|
es-set-tostringtag: 2.1.0
|
||||||
|
mime-types: 2.1.35
|
||||||
|
|
||||||
fraction.js@4.3.7: {}
|
fraction.js@4.3.7: {}
|
||||||
|
|
||||||
from@0.1.7: {}
|
from@0.1.7: {}
|
||||||
@@ -7530,6 +7628,24 @@ snapshots:
|
|||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
|
|
||||||
|
get-intrinsic@1.3.0:
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
es-define-property: 1.0.1
|
||||||
|
es-errors: 1.3.0
|
||||||
|
es-object-atoms: 1.1.1
|
||||||
|
function-bind: 1.1.2
|
||||||
|
get-proto: 1.0.1
|
||||||
|
gopd: 1.2.0
|
||||||
|
has-symbols: 1.1.0
|
||||||
|
hasown: 2.0.2
|
||||||
|
math-intrinsics: 1.1.0
|
||||||
|
|
||||||
|
get-proto@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
dunder-proto: 1.0.1
|
||||||
|
es-object-atoms: 1.1.1
|
||||||
|
|
||||||
get-stream@5.2.0:
|
get-stream@5.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
pump: 3.0.0
|
pump: 3.0.0
|
||||||
@@ -7629,6 +7745,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.2.4
|
||||||
|
|
||||||
|
gopd@1.2.0: {}
|
||||||
|
|
||||||
graceful-fs@4.2.11: {}
|
graceful-fs@4.2.11: {}
|
||||||
|
|
||||||
graphemer@1.4.0: {}
|
graphemer@1.4.0: {}
|
||||||
@@ -7654,9 +7772,11 @@ snapshots:
|
|||||||
|
|
||||||
has-symbols@1.0.3: {}
|
has-symbols@1.0.3: {}
|
||||||
|
|
||||||
|
has-symbols@1.1.0: {}
|
||||||
|
|
||||||
has-tostringtag@1.0.2:
|
has-tostringtag@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.1.0
|
||||||
|
|
||||||
has-unicode@2.0.1: {}
|
has-unicode@2.0.1: {}
|
||||||
|
|
||||||
@@ -7887,7 +8007,7 @@ snapshots:
|
|||||||
is-weakset@2.0.3:
|
is-weakset@2.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.7
|
call-bind: 1.0.7
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
|
|
||||||
is-windows@1.0.2: {}
|
is-windows@1.0.2: {}
|
||||||
|
|
||||||
@@ -8135,6 +8255,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist: 3.1.1
|
yallist: 3.1.1
|
||||||
|
|
||||||
|
lucide-react@0.469.0(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
|
||||||
lz-string@1.5.0: {}
|
lz-string@1.5.0: {}
|
||||||
|
|
||||||
magic-string@0.30.12:
|
magic-string@0.30.12:
|
||||||
@@ -8154,6 +8278,8 @@ snapshots:
|
|||||||
|
|
||||||
map-stream@0.1.0: {}
|
map-stream@0.1.0: {}
|
||||||
|
|
||||||
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
meow@13.2.0: {}
|
meow@13.2.0: {}
|
||||||
|
|
||||||
merge-stream@2.0.0: {}
|
merge-stream@2.0.0: {}
|
||||||
@@ -8224,23 +8350,23 @@ snapshots:
|
|||||||
|
|
||||||
negotiator@1.0.0: {}
|
negotiator@1.0.0: {}
|
||||||
|
|
||||||
next-intl@3.25.1(next@15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0):
|
next-intl@3.25.1(next@15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formatjs/intl-localematcher': 0.5.4
|
'@formatjs/intl-localematcher': 0.5.4
|
||||||
negotiator: 1.0.0
|
negotiator: 1.0.0
|
||||||
next: 15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
next: 15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
use-intl: 3.25.1(react@19.0.0)
|
use-intl: 3.25.1(react@19.0.0)
|
||||||
|
|
||||||
next-themes@0.2.1(next@15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
next-themes@0.2.1(next@15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
next: 15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7)
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
react-dom: 19.0.0(react@19.0.0)
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
|
||||||
next@15.2.0-canary.33(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7):
|
next@15.3.1-canary.9(@babel/core@7.26.0)(@playwright/test@1.48.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.80.7):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.2.0-canary.33
|
'@next/env': 15.3.1-canary.9
|
||||||
'@swc/counter': 0.1.3
|
'@swc/counter': 0.1.3
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
busboy: 1.6.0
|
busboy: 1.6.0
|
||||||
@@ -8250,17 +8376,17 @@ snapshots:
|
|||||||
react-dom: 19.0.0(react@19.0.0)
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.0.0)
|
styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.0.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@next/swc-darwin-arm64': 15.2.0-canary.33
|
'@next/swc-darwin-arm64': 15.3.1-canary.9
|
||||||
'@next/swc-darwin-x64': 15.2.0-canary.33
|
'@next/swc-darwin-x64': 15.3.1-canary.9
|
||||||
'@next/swc-linux-arm64-gnu': 15.2.0-canary.33
|
'@next/swc-linux-arm64-gnu': 15.3.1-canary.9
|
||||||
'@next/swc-linux-arm64-musl': 15.2.0-canary.33
|
'@next/swc-linux-arm64-musl': 15.3.1-canary.9
|
||||||
'@next/swc-linux-x64-gnu': 15.2.0-canary.33
|
'@next/swc-linux-x64-gnu': 15.3.1-canary.9
|
||||||
'@next/swc-linux-x64-musl': 15.2.0-canary.33
|
'@next/swc-linux-x64-musl': 15.3.1-canary.9
|
||||||
'@next/swc-win32-arm64-msvc': 15.2.0-canary.33
|
'@next/swc-win32-arm64-msvc': 15.3.1-canary.9
|
||||||
'@next/swc-win32-x64-msvc': 15.2.0-canary.33
|
'@next/swc-win32-x64-msvc': 15.3.1-canary.9
|
||||||
'@playwright/test': 1.48.2
|
'@playwright/test': 1.48.2
|
||||||
sass: 1.80.7
|
sass: 1.80.7
|
||||||
sharp: 0.33.5
|
sharp: 0.34.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
@@ -8800,6 +8926,9 @@ snapshots:
|
|||||||
|
|
||||||
semver@7.6.3: {}
|
semver@7.6.3: {}
|
||||||
|
|
||||||
|
semver@7.7.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
server-only@0.0.1: {}
|
server-only@0.0.1: {}
|
||||||
|
|
||||||
set-blocking@2.0.0: {}
|
set-blocking@2.0.0: {}
|
||||||
@@ -8820,31 +8949,32 @@ snapshots:
|
|||||||
functions-have-names: 1.2.3
|
functions-have-names: 1.2.3
|
||||||
has-property-descriptors: 1.0.2
|
has-property-descriptors: 1.0.2
|
||||||
|
|
||||||
sharp@0.33.5:
|
sharp@0.34.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color: 4.2.3
|
color: 4.2.3
|
||||||
detect-libc: 2.0.3
|
detect-libc: 2.0.3
|
||||||
semver: 7.6.3
|
semver: 7.7.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-darwin-arm64': 0.33.5
|
'@img/sharp-darwin-arm64': 0.34.1
|
||||||
'@img/sharp-darwin-x64': 0.33.5
|
'@img/sharp-darwin-x64': 0.34.1
|
||||||
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
'@img/sharp-libvips-darwin-arm64': 1.1.0
|
||||||
'@img/sharp-libvips-darwin-x64': 1.0.4
|
'@img/sharp-libvips-darwin-x64': 1.1.0
|
||||||
'@img/sharp-libvips-linux-arm': 1.0.5
|
'@img/sharp-libvips-linux-arm': 1.1.0
|
||||||
'@img/sharp-libvips-linux-arm64': 1.0.4
|
'@img/sharp-libvips-linux-arm64': 1.1.0
|
||||||
'@img/sharp-libvips-linux-s390x': 1.0.4
|
'@img/sharp-libvips-linux-ppc64': 1.1.0
|
||||||
'@img/sharp-libvips-linux-x64': 1.0.4
|
'@img/sharp-libvips-linux-s390x': 1.1.0
|
||||||
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
|
'@img/sharp-libvips-linux-x64': 1.1.0
|
||||||
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
|
'@img/sharp-libvips-linuxmusl-arm64': 1.1.0
|
||||||
'@img/sharp-linux-arm': 0.33.5
|
'@img/sharp-libvips-linuxmusl-x64': 1.1.0
|
||||||
'@img/sharp-linux-arm64': 0.33.5
|
'@img/sharp-linux-arm': 0.34.1
|
||||||
'@img/sharp-linux-s390x': 0.33.5
|
'@img/sharp-linux-arm64': 0.34.1
|
||||||
'@img/sharp-linux-x64': 0.33.5
|
'@img/sharp-linux-s390x': 0.34.1
|
||||||
'@img/sharp-linuxmusl-arm64': 0.33.5
|
'@img/sharp-linux-x64': 0.34.1
|
||||||
'@img/sharp-linuxmusl-x64': 0.33.5
|
'@img/sharp-linuxmusl-arm64': 0.34.1
|
||||||
'@img/sharp-wasm32': 0.33.5
|
'@img/sharp-linuxmusl-x64': 0.34.1
|
||||||
'@img/sharp-win32-ia32': 0.33.5
|
'@img/sharp-wasm32': 0.34.1
|
||||||
'@img/sharp-win32-x64': 0.33.5
|
'@img/sharp-win32-ia32': 0.34.1
|
||||||
|
'@img/sharp-win32-x64': 0.34.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
shebang-command@1.2.0:
|
shebang-command@1.2.0:
|
||||||
@@ -9480,7 +9610,7 @@ snapshots:
|
|||||||
|
|
||||||
wait-on@8.0.1(debug@4.3.7):
|
wait-on@8.0.1(debug@4.3.7):
|
||||||
dependencies:
|
dependencies:
|
||||||
axios: 1.7.7(debug@4.3.7)
|
axios: 1.8.3(debug@4.3.7)
|
||||||
joi: 17.13.3
|
joi: 17.13.3
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
minimist: 1.2.8
|
minimist: 1.2.8
|
||||||
|
Reference in New Issue
Block a user