mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-13 16:35:53 +00:00
Merge branch 'main' into qa
This commit is contained in:
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
@@ -7,6 +7,7 @@
|
|||||||
- [ ] All open todos and follow ups are defined in a new ticket and justified
|
- [ ] All open todos and follow ups are defined in a new ticket and justified
|
||||||
- [ ] Deviations from the acceptance criteria and design are agreed with the PO and documented.
|
- [ ] Deviations from the acceptance criteria and design are agreed with the PO and documented.
|
||||||
- [ ] Vitest unit tests ensure that components produce expected outputs on different inputs.
|
- [ ] Vitest unit tests ensure that components produce expected outputs on different inputs.
|
||||||
- [ ] Cypress integration tests ensure that login app pages work as expected. The ZITADEL API is mocked.
|
- [ ] Cypress integration tests ensure that login app pages work as expected on good and bad user inputs, ZITADEL responses or IDP redirects. The ZITADEL API is mocked, IDP redirects are simulated.
|
||||||
|
- [ ] Playwright acceptances tests ensure that the happy paths of common user journeys work as expected. The ZITADEL API is not mocked but IDP redirects are simulated.
|
||||||
- [ ] No debug or dead code
|
- [ ] No debug or dead code
|
||||||
- [ ] My code has no repetitions
|
- [ ] My code has no repetitions
|
||||||
|
@@ -7,8 +7,9 @@ import {
|
|||||||
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 { useRouter } from "next/navigation";
|
||||||
import { ReactNode, useState } from "react";
|
import { ReactNode, useCallback, useState } from "react";
|
||||||
import { Alert } from "./alert";
|
import { Alert } from "./alert";
|
||||||
|
import { SignInWithIdentityProviderProps } from "./idps/base-button";
|
||||||
import { SignInWithApple } from "./idps/sign-in-with-apple";
|
import { SignInWithApple } from "./idps/sign-in-with-apple";
|
||||||
import { SignInWithAzureAd } from "./idps/sign-in-with-azure-ad";
|
import { SignInWithAzureAd } from "./idps/sign-in-with-azure-ad";
|
||||||
import { SignInWithGeneric } from "./idps/sign-in-with-generic";
|
import { SignInWithGeneric } from "./idps/sign-in-with-generic";
|
||||||
@@ -29,39 +30,24 @@ export function SignInWithIdp({
|
|||||||
authRequestId,
|
authRequestId,
|
||||||
organization,
|
organization,
|
||||||
linkOnly,
|
linkOnly,
|
||||||
}: SignInWithIDPProps) {
|
}: Readonly<SignInWithIDPProps>) {
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
async function startFlow(idpId: string, provider: string) {
|
const startFlow = useCallback(
|
||||||
|
async (idpId: string, provider: string) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
|
if (linkOnly) params.set("link", "true");
|
||||||
|
if (authRequestId) params.set("authRequestId", authRequestId);
|
||||||
|
if (organization) params.set("organization", organization);
|
||||||
|
|
||||||
if (linkOnly) {
|
try {
|
||||||
params.set("link", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authRequestId) {
|
|
||||||
params.set("authRequestId", authRequestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (organization) {
|
|
||||||
params.set("organization", organization);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await startIDPFlow({
|
const response = await startIDPFlow({
|
||||||
idpId,
|
idpId,
|
||||||
successUrl: `/idp/${provider}/success?` + new URLSearchParams(params),
|
successUrl: `/idp/${provider}/success?` + params.toString(),
|
||||||
failureUrl: `/idp/${provider}/failure?` + new URLSearchParams(params),
|
failureUrl: `/idp/${provider}/failure?` + params.toString(),
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setError("Could not start IDP flow");
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response && "error" in response && response?.error) {
|
if (response && "error" in response && response?.error) {
|
||||||
@@ -72,141 +58,51 @@ export function SignInWithIdp({
|
|||||||
if (response && "redirect" in response && response?.redirect) {
|
if (response && "redirect" in response && response?.redirect) {
|
||||||
return router.push(response.redirect);
|
return router.push(response.redirect);
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
setError("Could not start IDP flow");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
[authRequestId, organization, linkOnly, router],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
const renderIDPButton = (idp: IdentityProvider) => {
|
||||||
<div className="flex flex-col w-full space-y-2 text-sm">
|
const { id, name, type } = idp;
|
||||||
{identityProviders &&
|
const onClick = () => startFlow(id, idpTypeToSlug(type));
|
||||||
identityProviders
|
|
||||||
/* - TODO: Implement after https://github.com/zitadel/zitadel/issues/8981 */
|
/* - TODO: Implement after https://github.com/zitadel/zitadel/issues/8981 */
|
||||||
|
|
||||||
// .filter((idp) =>
|
// .filter((idp) =>
|
||||||
// linkOnly ? idp.config?.options.isLinkingAllowed : true,
|
// linkOnly ? idp.config?.options.isLinkingAllowed : true,
|
||||||
// )
|
// )
|
||||||
.map((idp, i) => {
|
const components: Partial<
|
||||||
switch (idp.type) {
|
Record<
|
||||||
case IdentityProviderType.APPLE:
|
IdentityProviderType,
|
||||||
|
(props: SignInWithIdentityProviderProps) => ReactNode
|
||||||
|
>
|
||||||
|
> = {
|
||||||
|
[IdentityProviderType.APPLE]: SignInWithApple,
|
||||||
|
[IdentityProviderType.OAUTH]: SignInWithGeneric,
|
||||||
|
[IdentityProviderType.OIDC]: SignInWithGeneric,
|
||||||
|
[IdentityProviderType.GITHUB]: SignInWithGithub,
|
||||||
|
[IdentityProviderType.GITHUB_ES]: SignInWithGithub,
|
||||||
|
[IdentityProviderType.AZURE_AD]: SignInWithAzureAd,
|
||||||
|
[IdentityProviderType.GOOGLE]: (props) => (
|
||||||
|
<SignInWithGoogle {...props} e2e="google" />
|
||||||
|
),
|
||||||
|
[IdentityProviderType.GITLAB]: SignInWithGitlab,
|
||||||
|
[IdentityProviderType.GITLAB_SELF_HOSTED]: SignInWithGitlab,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Component = components[type];
|
||||||
|
return Component ? (
|
||||||
|
<Component key={id} name={name} onClick={onClick} />
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SignInWithApple
|
<div className="flex flex-col w-full space-y-2 text-sm">
|
||||||
key={`idp-${i}`}
|
{identityProviders?.map(renderIDPButton)}
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.APPLE),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithApple>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.OAUTH:
|
|
||||||
return (
|
|
||||||
<SignInWithGeneric
|
|
||||||
key={`idp-${i}`}
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.OAUTH),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithGeneric>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.OIDC:
|
|
||||||
return (
|
|
||||||
<SignInWithGeneric
|
|
||||||
key={`idp-${i}`}
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.OIDC),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithGeneric>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.GITHUB:
|
|
||||||
return (
|
|
||||||
<SignInWithGithub
|
|
||||||
key={`idp-${i}`}
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.GITHUB),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithGithub>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.GITHUB_ES:
|
|
||||||
return (
|
|
||||||
<SignInWithGithub
|
|
||||||
key={`idp-${i}`}
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.GITHUB_ES),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithGithub>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.AZURE_AD:
|
|
||||||
return (
|
|
||||||
<SignInWithAzureAd
|
|
||||||
key={`idp-${i}`}
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.AZURE_AD),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithAzureAd>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.GOOGLE:
|
|
||||||
return (
|
|
||||||
<SignInWithGoogle
|
|
||||||
key={`idp-${i}`}
|
|
||||||
e2e="google"
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.GOOGLE),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithGoogle>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.GITLAB:
|
|
||||||
return (
|
|
||||||
<SignInWithGitlab
|
|
||||||
key={`idp-${i}`}
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.GITLAB),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithGitlab>
|
|
||||||
);
|
|
||||||
case IdentityProviderType.GITLAB_SELF_HOSTED:
|
|
||||||
return (
|
|
||||||
<SignInWithGitlab
|
|
||||||
key={`idp-${i}`}
|
|
||||||
name={idp.name}
|
|
||||||
onClick={() =>
|
|
||||||
startFlow(
|
|
||||||
idp.id,
|
|
||||||
idpTypeToSlug(IdentityProviderType.GITLAB_SELF_HOSTED),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></SignInWithGitlab>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<Alert>{error}</Alert>
|
<Alert>{error}</Alert>
|
||||||
|
Reference in New Issue
Block a user