Merge branch 'main' into login-eslint

This commit is contained in:
Max Peintner
2025-07-03 14:07:13 +02:00
committed by GitHub
10 changed files with 81 additions and 46 deletions

View File

@@ -86,7 +86,7 @@ jobs:
actions: write actions: write
id-token: write id-token: write
with: with:
ignore-run-cache: ${{ github.event_name == 'workflow_dispatch' }} ignore-run-cache: ${{ github.event_name == 'workflow_dispatch' || fromJSON(github.run_attempt) > 1 }}
node_version: "20" node_version: "20"
container: container:
@@ -106,7 +106,7 @@ jobs:
packages: write packages: write
id-token: write id-token: write
with: with:
login_build_image_name: "ghcr.io/zitadel/login-build" login_build_image_name: "ghcr.io/zitadel/zitadel-login-build"
node_version: "20" node_version: "20"
e2e: e2e:
@@ -133,5 +133,5 @@ jobs:
image_name: "ghcr.io/zitadel/zitadel" image_name: "ghcr.io/zitadel/zitadel"
google_image_name: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel" google_image_name: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel"
build_image_name_login: ${{ needs.login-container.outputs.login_build_image }} build_image_name_login: ${{ needs.login-container.outputs.login_build_image }}
image_name_login: "ghcr.io/zitadel/login" image_name_login: "ghcr.io/zitadel/zitadel-login"
google_image_name_login: europe-docker.pkg.dev/zitadel-common/zitadel-repo/login google_image_name_login: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel-login"

View File

@@ -22,6 +22,7 @@ env:
default_labels: | default_labels: |
org.opencontainers.image.documentation=https://zitadel.com/docs org.opencontainers.image.documentation=https://zitadel.com/docs
org.opencontainers.image.vendor=CAOS AG org.opencontainers.image.vendor=CAOS AG
org.opencontainers.image.licenses=MIT
jobs: jobs:
login-container: login-container:
@@ -29,6 +30,7 @@ jobs:
runs-on: depot-ubuntu-22.04-8 runs-on: depot-ubuntu-22.04-8
permissions: permissions:
id-token: write id-token: write
packages: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: depot/setup-action@v1 - uses: depot/setup-action@v1
@@ -40,6 +42,8 @@ jobs:
with: with:
images: ${{ inputs.login_build_image_name }} images: ${{ inputs.login_build_image_name }}
labels: ${{ env.default_labels}} labels: ${{ env.default_labels}}
annotations: |
manifest:org.opencontainers.image.licenses=MIT
tags: | tags: |
type=sha,prefix=,suffix=,format=long type=sha,prefix=,suffix=,format=long
- name: Login to Docker registry - name: Login to Docker registry
@@ -53,11 +57,14 @@ jobs:
env: env:
NODE_VERSION: ${{ inputs.node_version }} NODE_VERSION: ${{ inputs.node_version }}
with: with:
workdir: login
push: true push: true
provenance: true
sbom: true
targets: login-standalone targets: login-standalone
set: login-standalone.platforms=[linux/amd64,linux/arm64] set: login-*.context=./login/
project: w47wkxzdtw project: w47wkxzdtw
files: | files: |
./login/docker-bake.hcl
./login/docker-bake-release.hcl
./docker-bake.hcl ./docker-bake.hcl
cwd://${{ steps.login-meta.outputs.bake-file }} cwd://${{ steps.login-meta.outputs.bake-file }}

View File

@@ -14,7 +14,7 @@ export GID := $(id -g)
export LOGIN_TEST_ACCEPTANCE_BUILD_CONTEXT := $(LOGIN_DIR)apps/login-test-acceptance export LOGIN_TEST_ACCEPTANCE_BUILD_CONTEXT := $(LOGIN_DIR)apps/login-test-acceptance
export DOCKER_METADATA_OUTPUT_VERSION ?= local export DOCKER_METADATA_OUTPUT_VERSION ?= local
export LOGIN_TAG ?= login:${DOCKER_METADATA_OUTPUT_VERSION} export LOGIN_TAG ?= zitadel-login:${DOCKER_METADATA_OUTPUT_VERSION}
export LOGIN_TEST_UNIT_TAG := login-test-unit:${DOCKER_METADATA_OUTPUT_VERSION} export LOGIN_TEST_UNIT_TAG := login-test-unit:${DOCKER_METADATA_OUTPUT_VERSION}
export LOGIN_TEST_INTEGRATION_TAG := login-test-integration:${DOCKER_METADATA_OUTPUT_VERSION} export LOGIN_TEST_INTEGRATION_TAG := login-test-integration:${DOCKER_METADATA_OUTPUT_VERSION}
export LOGIN_TEST_ACCEPTANCE_TAG := login-test-acceptance:${DOCKER_METADATA_OUTPUT_VERSION} export LOGIN_TEST_ACCEPTANCE_TAG := login-test-acceptance:${DOCKER_METADATA_OUTPUT_VERSION}

View File

@@ -16,7 +16,7 @@ services:
ZITADEL_ADMIN_USER: zitadel-admin@zitadel.traefik ZITADEL_ADMIN_USER: zitadel-admin@zitadel.traefik
login: login:
image: "${LOGIN_TAG:-login:local}" image: "${LOGIN_TAG:-zitadel-login:local}"
container_name: acceptance-login container_name: acceptance-login
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"

View File

@@ -61,7 +61,7 @@ export default async function Page(props: {
return ( return (
<DynamicTheme branding={branding}> <DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4"> <div className="flex flex-col items-center space-y-4">
<h1 data-i18n-key="error.tryagain"> <h1>
<Translated i18nKey="title" namespace="loginname" /> <Translated i18nKey="title" namespace="loginname" />
</h1> </h1>
<p className="ztdl-p"> <p className="ztdl-p">

View File

@@ -291,23 +291,25 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}; };
} }
const paramsPassword: any = { const paramsPassword = new URLSearchParams({
loginName: session.factors?.user?.loginName, loginName: session.factors?.user?.loginName,
}; });
// TODO: does this have to be checked in loginSettings.allowDomainDiscovery // TODO: does this have to be checked in loginSettings.allowDomainDiscovery
if (command.organization || session.factors?.user?.organizationId) { if (command.organization || session.factors?.user?.organizationId) {
paramsPassword.organization = paramsPassword.append(
command.organization ?? session.factors?.user?.organizationId; "organization",
command.organization ?? session.factors?.user?.organizationId,
);
} }
if (command.requestId) { if (command.requestId) {
paramsPassword.requestId = command.requestId; paramsPassword.append("requestId", command.requestId);
} }
return { return {
redirect: "/password?" + new URLSearchParams(paramsPassword), redirect: "/password?" + paramsPassword,
}; };
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
@@ -318,36 +320,42 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}; };
} }
const paramsPasskey: any = { loginName: command.loginName }; const paramsPasskey = new URLSearchParams({
loginName: session.factors?.user?.loginName,
});
if (command.requestId) { if (command.requestId) {
paramsPasskey.requestId = command.requestId; paramsPasskey.append("requestId", command.requestId);
} }
if (command.organization || session.factors?.user?.organizationId) { if (command.organization || session.factors?.user?.organizationId) {
paramsPasskey.organization = paramsPasskey.append(
command.organization ?? session.factors?.user?.organizationId; "organization",
command.organization ?? session.factors?.user?.organizationId,
);
} }
return { redirect: "/passkey?" + new URLSearchParams(paramsPasskey) }; return { redirect: "/passkey?" + paramsPasskey };
} }
} else { } else {
// prefer passkey in favor of other methods // prefer passkey in favor of other methods
if (methods.authMethodTypes.includes(AuthenticationMethodType.PASSKEY)) { if (methods.authMethodTypes.includes(AuthenticationMethodType.PASSKEY)) {
const passkeyParams: any = { const passkeyParams = new URLSearchParams({
loginName: command.loginName, loginName: session.factors?.user?.loginName,
altPassword: `${methods.authMethodTypes.includes(1)}`, // show alternative password option altPassword: `${methods.authMethodTypes.includes(1)}`, // show alternative password option
}; });
if (command.requestId) { if (command.requestId) {
passkeyParams.requestId = command.requestId; passkeyParams.append("requestId", command.requestId);
} }
if (command.organization || session.factors?.user?.organizationId) { if (command.organization || session.factors?.user?.organizationId) {
passkeyParams.organization = passkeyParams.append(
command.organization ?? session.factors?.user?.organizationId; "organization",
command.organization ?? session.factors?.user?.organizationId,
);
} }
return { redirect: "/passkey?" + new URLSearchParams(passkeyParams) }; return { redirect: "/passkey?" + passkeyParams };
} else if ( } else if (
methods.authMethodTypes.includes(AuthenticationMethodType.IDP) methods.authMethodTypes.includes(AuthenticationMethodType.IDP)
) { ) {
@@ -356,19 +364,23 @@ export async function sendLoginname(command: SendLoginnameCommand) {
methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD) methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD)
) { ) {
// user has no passkey setup and login settings allow passkeys // user has no passkey setup and login settings allow passkeys
const paramsPasswordDefault: any = { loginName: command.loginName }; const paramsPasswordDefault = new URLSearchParams({
loginName: session.factors?.user?.loginName,
});
if (command.requestId) { if (command.requestId) {
paramsPasswordDefault.requestId = command.requestId; paramsPasswordDefault.append("requestId", command.requestId);
} }
if (command.organization || session.factors?.user?.organizationId) { if (command.organization || session.factors?.user?.organizationId) {
paramsPasswordDefault.organization = paramsPasswordDefault.append(
command.organization ?? session.factors?.user?.organizationId; "organization",
command.organization ?? session.factors?.user?.organizationId,
);
} }
return { return {
redirect: "/password?" + new URLSearchParams(paramsPasswordDefault), redirect: "/password?" + paramsPasswordDefault,
}; };
} }
} }

View File

@@ -13,7 +13,6 @@ import {
type LoadMostRecentSessionParams = { type LoadMostRecentSessionParams = {
serviceUrl: string; serviceUrl: string;
sessionParams: { sessionParams: {
loginName?: string; loginName?: string;
organization?: string; organization?: string;

View File

@@ -854,15 +854,15 @@ export async function searchUsers({
const emailQuery = EmailQuery(searchValue); const emailQuery = EmailQuery(searchValue);
emailAndPhoneQueries.push(emailQuery); emailAndPhoneQueries.push(emailQuery);
} else { } else {
const emailAndPhoneOrQueries: SearchQuery[] = []; const orQuery: SearchQuery[] = [];
const emailQuery = EmailQuery(searchValue); const emailQuery = EmailQuery(searchValue);
emailAndPhoneOrQueries.push(emailQuery); orQuery.push(emailQuery);
let phoneQuery; let phoneQuery;
if (searchValue.length <= 20) { if (searchValue.length <= 20) {
phoneQuery = PhoneQuery(searchValue); phoneQuery = PhoneQuery(searchValue);
emailAndPhoneOrQueries.push(phoneQuery); orQuery.push(phoneQuery);
} }
emailAndPhoneQueries.push( emailAndPhoneQueries.push(
@@ -870,7 +870,7 @@ export async function searchUsers({
query: { query: {
case: "orQuery", case: "orQuery",
value: { value: {
queries: emailAndPhoneOrQueries, queries: orQuery,
}, },
}, },
}), }),
@@ -903,7 +903,7 @@ export async function searchUsers({
} }
if (emailOrPhoneResult.result.length == 1) { if (emailOrPhoneResult.result.length == 1) {
return loginNameResult; return emailOrPhoneResult;
} }
return { error: "User not found in the system" }; return { error: "User not found in the system" };

View File

@@ -0,0 +1,3 @@
target "release" {
platforms = ["linux/amd64", "linux/arm64"]
}

View File

@@ -6,12 +6,18 @@ variable "DOCKERFILES_DIR" {
default = "dockerfiles/" default = "dockerfiles/"
} }
# The release target is overwritten in docker-bake-release.hcl
# It makes sure the image is built for multiple platforms.
# By default the platforms property is empty, so images are only built for the current bake runtime platform.
target "release" {}
# typescript-proto-client is used to generate the client code for the login service. # typescript-proto-client is used to generate the client code for the login service.
# It is not login-prefixed, so it is easily extendable. # It is not login-prefixed, so it is easily extendable.
# To extend this bake-file.hcl, set the context of all login-prefixed targets to a different directory. # To extend this bake-file.hcl, set the context of all login-prefixed targets to a different directory.
# For example docker bake --file login/docker-bake.hcl --file docker-bake.hcl --set login-*.context=./login/ # For example docker bake --file login/docker-bake.hcl --file docker-bake.hcl --set login-*.context=./login/
# The zitadel repository uses this to generate the client and the mock server from local proto files. # The zitadel repository uses this to generate the client and the mock server from local proto files.
target "typescript-proto-client" { target "typescript-proto-client" {
inherits = ["release"]
dockerfile = "${DOCKERFILES_DIR}typescript-proto-client.Dockerfile" dockerfile = "${DOCKERFILES_DIR}typescript-proto-client.Dockerfile"
contexts = { contexts = {
# We directly generate and download the client server-side with buf, so we don't need the proto files # We directly generate and download the client server-side with buf, so we don't need the proto files
@@ -37,6 +43,7 @@ target "login-typescript-proto-client-out" {
# For example docker bake --file login/docker-bake.hcl --file docker-bake.hcl --set login-*.context=./login/ # For example docker bake --file login/docker-bake.hcl --file docker-bake.hcl --set login-*.context=./login/
# The zitadel repository uses this to generate the client and the mock server from local proto files. # The zitadel repository uses this to generate the client and the mock server from local proto files.
target "proto-files" { target "proto-files" {
inherits = ["release"]
dockerfile = "${DOCKERFILES_DIR}proto-files.Dockerfile" dockerfile = "${DOCKERFILES_DIR}proto-files.Dockerfile"
contexts = { contexts = {
login-pnpm = "target:login-pnpm" login-pnpm = "target:login-pnpm"
@@ -48,6 +55,7 @@ variable "NODE_VERSION" {
} }
target "login-pnpm" { target "login-pnpm" {
inherits = ["release"]
dockerfile = "${DOCKERFILES_DIR}login-pnpm.Dockerfile" dockerfile = "${DOCKERFILES_DIR}login-pnpm.Dockerfile"
args = { args = {
NODE_VERSION = "${NODE_VERSION}" NODE_VERSION = "${NODE_VERSION}"
@@ -76,6 +84,7 @@ target "login-test-unit" {
} }
target "login-client" { target "login-client" {
inherits = ["release"]
dockerfile = "${DOCKERFILES_DIR}login-client.Dockerfile" dockerfile = "${DOCKERFILES_DIR}login-client.Dockerfile"
contexts = { contexts = {
login-pnpm = "target:login-pnpm" login-pnpm = "target:login-pnpm"
@@ -93,7 +102,7 @@ target "core-mock" {
contexts = { contexts = {
protos = "target:proto-files" protos = "target:proto-files"
} }
tags = ["${LOGIN_CORE_MOCK_TAG}"] tags = ["${LOGIN_CORE_MOCK_TAG}"]
} }
variable "LOGIN_TEST_INTEGRATION_TAG" { variable "LOGIN_TEST_INTEGRATION_TAG" {
@@ -105,7 +114,7 @@ target "login-test-integration" {
contexts = { contexts = {
login-pnpm = "target:login-pnpm" login-pnpm = "target:login-pnpm"
} }
tags = ["${LOGIN_TEST_INTEGRATION_TAG}"] tags = ["${LOGIN_TEST_INTEGRATION_TAG}"]
} }
variable "LOGIN_TEST_ACCEPTANCE_TAG" { variable "LOGIN_TEST_ACCEPTANCE_TAG" {
@@ -117,28 +126,33 @@ target "login-test-acceptance" {
contexts = { contexts = {
login-pnpm = "target:login-pnpm" login-pnpm = "target:login-pnpm"
} }
tags = ["${LOGIN_TEST_ACCEPTANCE_TAG}"] tags = ["${LOGIN_TEST_ACCEPTANCE_TAG}"]
} }
variable "LOGIN_TAG" { variable "LOGIN_TAG" {
default = "zitadel-login:local" default = "zitadel-login:local"
} }
target "docker-metadata-action" {} target "docker-metadata-action" {
# In the pipeline, this target is overwritten by the docker metadata action.
tags = ["${LOGIN_TAG}"]
}
# We run integration and acceptance tests against the next standalone server for docker. # We run integration and acceptance tests against the next standalone server for docker.
target "login-standalone" { target "login-standalone" {
inherits = ["docker-metadata-action"] inherits = [
"docker-metadata-action",
"release",
]
dockerfile = "${DOCKERFILES_DIR}login-standalone.Dockerfile" dockerfile = "${DOCKERFILES_DIR}login-standalone.Dockerfile"
contexts = { contexts = {
login-client = "target:login-client" login-client = "target:login-client"
} }
tags = ["${LOGIN_TAG}"]
} }
target "login-standalone-out" { target "login-standalone-out" {
inherits = ["login-standalone"] inherits = ["login-standalone"]
target = "login-standalone-out" target = "login-standalone-out"
output = [ output = [
"type=local,dest=${LOGIN_DIR}apps/login/standalone" "type=local,dest=${LOGIN_DIR}apps/login/standalone"
] ]