mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-11 21:52:32 +00:00
Merge pull request #116 from zitadel/qa
middleware: public host, instance host
This commit is contained in:
@@ -1,13 +1,22 @@
|
|||||||
import { ProviderSlug } from "@/lib/demos";
|
import { getBrandingSettings } from "@/lib/zitadel";
|
||||||
import { getBrandingSettings, PROVIDER_NAME_MAPPING } from "@/lib/zitadel";
|
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
|
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
|
|
||||||
|
// This configuration shows the given name in the respective IDP button as fallback
|
||||||
|
const PROVIDER_NAME_MAPPING: {
|
||||||
|
[provider: string]: string;
|
||||||
|
} = {
|
||||||
|
[IdentityProviderType.GOOGLE]: "Google",
|
||||||
|
[IdentityProviderType.GITHUB]: "GitHub",
|
||||||
|
[IdentityProviderType.AZURE_AD]: "Microsoft",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
searchParams: Record<string | number | symbol, string | undefined>;
|
searchParams: Record<string | number | symbol, string | undefined>;
|
||||||
params: { provider: ProviderSlug };
|
params: { provider: string };
|
||||||
}) {
|
}) {
|
||||||
const { id, token, authRequestId, organization } = searchParams;
|
const { id, token, authRequestId, organization } = searchParams;
|
||||||
const { provider } = params;
|
const { provider } = params;
|
||||||
|
|||||||
@@ -1,142 +1,36 @@
|
|||||||
import { ProviderSlug } from "@/lib/demos";
|
import { PROVIDER_MAPPING } from "@/lib/idp";
|
||||||
import { getBrandingSettings, userService } from "@/lib/zitadel";
|
import {
|
||||||
|
addIDPLink,
|
||||||
|
createUser,
|
||||||
|
getBrandingSettings,
|
||||||
|
getIDPByID,
|
||||||
|
listUsers,
|
||||||
|
retrieveIDPIntent,
|
||||||
|
} from "@/lib/zitadel";
|
||||||
import Alert, { AlertType } from "@/ui/Alert";
|
import Alert, { AlertType } from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import IdpSignin from "@/ui/IdpSignin";
|
import IdpSignin from "@/ui/IdpSignin";
|
||||||
import { AddHumanUserRequest } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
import { AutoLinkingOption } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
|
||||||
import { IDPInformation, IDPLink } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
|
||||||
import { PartialMessage } from "@zitadel/client";
|
|
||||||
|
|
||||||
const PROVIDER_MAPPING: {
|
|
||||||
[provider: string]: (
|
|
||||||
rI: IDPInformation,
|
|
||||||
) => PartialMessage<AddHumanUserRequest>;
|
|
||||||
} = {
|
|
||||||
[ProviderSlug.GOOGLE]: (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation?.toJson() as {
|
|
||||||
User: {
|
|
||||||
email: string;
|
|
||||||
name?: string;
|
|
||||||
given_name?: string;
|
|
||||||
family_name?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const idpLink: PartialMessage<IDPLink> = {
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
};
|
|
||||||
|
|
||||||
const req: PartialMessage<AddHumanUserRequest> = {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo.User?.email,
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
// organisation: Organisation | undefined;
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo.User?.name ?? "",
|
|
||||||
givenName: rawInfo.User?.given_name ?? "",
|
|
||||||
familyName: rawInfo.User?.family_name ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [idpLink],
|
|
||||||
};
|
|
||||||
return req;
|
|
||||||
},
|
|
||||||
[ProviderSlug.AZURE]: (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation?.toJson() as {
|
|
||||||
mail: string;
|
|
||||||
displayName?: string;
|
|
||||||
givenName?: string;
|
|
||||||
surname?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const idpLink: PartialMessage<IDPLink> = {
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
};
|
|
||||||
|
|
||||||
const req: PartialMessage<AddHumanUserRequest> = {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo?.mail,
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
// organisation: Organisation | undefined;
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo?.displayName ?? "",
|
|
||||||
givenName: rawInfo?.givenName ?? "",
|
|
||||||
familyName: rawInfo?.surname ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [idpLink],
|
|
||||||
};
|
|
||||||
return req;
|
|
||||||
},
|
|
||||||
[ProviderSlug.GITHUB]: (idp: IDPInformation) => {
|
|
||||||
const rawInfo = idp.rawInformation?.toJson() as {
|
|
||||||
email: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
const idpLink: PartialMessage<IDPLink> = {
|
|
||||||
idpId: idp.idpId,
|
|
||||||
userId: idp.userId,
|
|
||||||
userName: idp.userName,
|
|
||||||
};
|
|
||||||
const req: PartialMessage<AddHumanUserRequest> = {
|
|
||||||
username: idp.userName,
|
|
||||||
email: {
|
|
||||||
email: rawInfo?.email,
|
|
||||||
verification: { case: "isVerified", value: true },
|
|
||||||
},
|
|
||||||
// organisation: Organisation | undefined;
|
|
||||||
profile: {
|
|
||||||
displayName: rawInfo?.name ?? "",
|
|
||||||
givenName: rawInfo?.name ?? "",
|
|
||||||
familyName: rawInfo?.name ?? "",
|
|
||||||
},
|
|
||||||
idpLinks: [idpLink],
|
|
||||||
};
|
|
||||||
return req;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function retrieveIDPIntent(id: string, token: string) {
|
|
||||||
return userService.retrieveIdentityProviderIntent(
|
|
||||||
{ idpIntentId: id, idpIntentToken: token },
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createUser(
|
|
||||||
provider: ProviderSlug,
|
|
||||||
info: IDPInformation,
|
|
||||||
): Promise<string> {
|
|
||||||
const userData = PROVIDER_MAPPING[provider](info);
|
|
||||||
return userService.addHumanUser(userData, {}).then((resp) => resp.userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
searchParams: Record<string | number | symbol, string | undefined>;
|
searchParams: Record<string | number | symbol, string | undefined>;
|
||||||
params: { provider: ProviderSlug };
|
params: { provider: string };
|
||||||
}) {
|
}) {
|
||||||
const { id, token, authRequestId, organization } = searchParams;
|
const { id, token, authRequestId, organization } = searchParams;
|
||||||
const { provider } = params;
|
const { provider } = params;
|
||||||
|
|
||||||
const branding = await getBrandingSettings(organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
if (provider && id && token) {
|
if (provider && id && token) {
|
||||||
return retrieveIDPIntent(id, token)
|
return retrieveIDPIntent(id, token)
|
||||||
.then((resp) => {
|
.then(async (resp) => {
|
||||||
const { idpInformation, userId } = resp;
|
const { idpInformation, userId } = resp;
|
||||||
|
|
||||||
if (idpInformation) {
|
|
||||||
// handle login
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
|
// TODO: update user if idp.options.isAutoUpdate is true
|
||||||
|
|
||||||
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">
|
||||||
@@ -151,24 +45,52 @@ export default async function Page({
|
|||||||
</div>
|
</div>
|
||||||
</DynamicTheme>
|
</DynamicTheme>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idpInformation) {
|
||||||
|
const idp = await getIDPByID(idpInformation.idpId);
|
||||||
|
const options = idp?.config?.options;
|
||||||
|
|
||||||
|
// search for potential user via username, then link
|
||||||
|
if (options?.isLinkingAllowed) {
|
||||||
|
let foundUser;
|
||||||
|
const email =
|
||||||
|
PROVIDER_MAPPING[provider](idpInformation).email?.email;
|
||||||
|
|
||||||
|
if (options.autoLinking === AutoLinkingOption.EMAIL && email) {
|
||||||
|
foundUser = await listUsers({ email }).then((response) => {
|
||||||
|
return response.result ? response.result[0] : null;
|
||||||
|
});
|
||||||
|
} else if (options.autoLinking === AutoLinkingOption.USERNAME) {
|
||||||
|
foundUser = await listUsers(
|
||||||
|
options.autoLinking === AutoLinkingOption.USERNAME
|
||||||
|
? { userName: idpInformation.userName }
|
||||||
|
: { email },
|
||||||
|
).then((response) => {
|
||||||
|
return response.result ? response.result[0] : null;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// handle register
|
foundUser = await listUsers({
|
||||||
return createUser(provider, idpInformation)
|
userName: idpInformation.userName,
|
||||||
.then((userId) => {
|
email,
|
||||||
|
}).then((response) => {
|
||||||
|
return response.result ? response.result[0] : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundUser) {
|
||||||
|
const idpLink = await addIDPLink(
|
||||||
|
{
|
||||||
|
id: idpInformation.idpId,
|
||||||
|
userId: idpInformation.userId,
|
||||||
|
userName: idpInformation.userName,
|
||||||
|
},
|
||||||
|
foundUser.userId,
|
||||||
|
).catch((error) => {
|
||||||
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>Register successful</h1>
|
<h1>Linking failed</h1>
|
||||||
<div>You have successfully been registered!</div>
|
|
||||||
</div>
|
|
||||||
</DynamicTheme>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return (
|
|
||||||
<DynamicTheme branding={branding}>
|
|
||||||
<div className="flex flex-col items-center space-y-4">
|
|
||||||
<h1>Register failed</h1>
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{
|
{
|
||||||
<Alert type={AlertType.ALERT}>
|
<Alert type={AlertType.ALERT}>
|
||||||
@@ -180,9 +102,66 @@ export default async function Page({
|
|||||||
</DynamicTheme>
|
</DynamicTheme>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (idpLink) {
|
||||||
|
return (
|
||||||
|
// TODO: possibily login user now
|
||||||
|
<DynamicTheme branding={branding}>
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<h1>Account successfully linked</h1>
|
||||||
|
<div>Your account has successfully been linked!</div>
|
||||||
|
</div>
|
||||||
|
</DynamicTheme>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.isCreationAllowed && options.isAutoCreation) {
|
||||||
|
const newUser = await createUser(provider, idpInformation);
|
||||||
|
|
||||||
|
if (newUser) {
|
||||||
|
return (
|
||||||
|
<DynamicTheme branding={branding}>
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<h1>Register successful</h1>
|
||||||
|
<div>You have successfully been registered!</div>
|
||||||
|
</div>
|
||||||
|
</DynamicTheme>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return login failed if no linking or creation is allowed and no user was found
|
||||||
|
return (
|
||||||
|
<DynamicTheme branding={branding}>
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<h1>Login failed</h1>
|
||||||
|
<div className="w-full">
|
||||||
|
{
|
||||||
|
<Alert type={AlertType.ALERT}>
|
||||||
|
User could not be logged in
|
||||||
|
</Alert>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DynamicTheme>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Could not get user information.");
|
return (
|
||||||
|
<DynamicTheme branding={branding}>
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<h1>Login failed</h1>
|
||||||
|
<div className="w-full">
|
||||||
|
{
|
||||||
|
<Alert type={AlertType.ALERT}>
|
||||||
|
Could not get user information
|
||||||
|
</Alert>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DynamicTheme>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ProviderSlug } from "@/lib/demos";
|
import { idpTypeToSlug } from "@/lib/idp";
|
||||||
import {
|
import {
|
||||||
getActiveIdentityProviders,
|
getActiveIdentityProviders,
|
||||||
getLoginSettings,
|
getLoginSettings,
|
||||||
@@ -7,14 +7,16 @@ import {
|
|||||||
startIdentityProviderFlow,
|
startIdentityProviderFlow,
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import { createSessionForUserIdAndUpdateCookie } from "@/utils/session";
|
import { createSessionForUserIdAndUpdateCookie } from "@/utils/session";
|
||||||
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
if (body) {
|
if (body) {
|
||||||
const { loginName, authRequestId, organization } = body;
|
const { loginName, authRequestId, organization } = body;
|
||||||
return listUsers(loginName, organization).then(async (users) => {
|
return listUsers({
|
||||||
|
userName: loginName,
|
||||||
|
organizationId: organization,
|
||||||
|
}).then(async (users) => {
|
||||||
if (users.details?.totalResult == BigInt(1) && users.result[0].userId) {
|
if (users.details?.totalResult == BigInt(1) && users.result[0].userId) {
|
||||||
const userId = users.result[0].userId;
|
const userId = users.result[0].userId;
|
||||||
return createSessionForUserIdAndUpdateCookie(
|
return createSessionForUserIdAndUpdateCookie(
|
||||||
@@ -64,28 +66,8 @@ export async function POST(request: NextRequest) {
|
|||||||
const host = request.nextUrl.origin;
|
const host = request.nextUrl.origin;
|
||||||
|
|
||||||
const identityProviderType = identityProviders[0].type;
|
const identityProviderType = identityProviders[0].type;
|
||||||
let provider: string;
|
|
||||||
|
|
||||||
switch (identityProviderType) {
|
const provider = idpTypeToSlug(identityProviderType);
|
||||||
case IdentityProviderType.GITHUB:
|
|
||||||
provider = "github";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.GOOGLE:
|
|
||||||
provider = "google";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.AZURE_AD:
|
|
||||||
provider = "azure";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.SAML:
|
|
||||||
provider = "saml";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.OIDC:
|
|
||||||
provider = "oidc";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
provider = "oidc";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,19 @@ export async function POST(request: NextRequest) {
|
|||||||
const session = await getSession(sessionCookie.id, sessionCookie.token);
|
const session = await getSession(sessionCookie.id, sessionCookie.token);
|
||||||
|
|
||||||
const userId = session?.session?.factors?.user?.id;
|
const userId = session?.session?.factors?.user?.id;
|
||||||
|
console.log("payload", {
|
||||||
if (userId) {
|
|
||||||
return verifyPasskeyRegistration(
|
|
||||||
passkeyId,
|
passkeyId,
|
||||||
passkeyName,
|
passkeyName,
|
||||||
publicKeyCredential,
|
publicKeyCredential,
|
||||||
userId,
|
userId,
|
||||||
)
|
});
|
||||||
|
if (userId) {
|
||||||
|
return verifyPasskeyRegistration({
|
||||||
|
passkeyId,
|
||||||
|
passkeyName,
|
||||||
|
publicKeyCredential,
|
||||||
|
userId,
|
||||||
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
return NextResponse.json(resp);
|
return NextResponse.json(resp);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ export async function POST(request: NextRequest) {
|
|||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
if (body) {
|
if (body) {
|
||||||
const { loginName, organization } = body;
|
const { loginName, organization } = body;
|
||||||
return listUsers(loginName, organization).then((users) => {
|
return listUsers({
|
||||||
|
userName: loginName,
|
||||||
|
organizationId: organization,
|
||||||
|
}).then((users) => {
|
||||||
if (
|
if (
|
||||||
users.details &&
|
users.details &&
|
||||||
Number(users.details.totalResult) == 1 &&
|
Number(users.details.totalResult) == 1 &&
|
||||||
|
|||||||
@@ -22,12 +22,15 @@ export async function POST(request: NextRequest) {
|
|||||||
const userId = session?.session?.factors?.user?.id;
|
const userId = session?.session?.factors?.user?.id;
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
const req: PlainMessage<VerifyU2FRegistrationRequest> = {
|
let req: PlainMessage<VerifyU2FRegistrationRequest> = {
|
||||||
publicKeyCredential,
|
publicKeyCredential,
|
||||||
u2fId,
|
u2fId,
|
||||||
userId,
|
userId,
|
||||||
tokenName: passkeyName,
|
tokenName: passkeyName,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
req = VerifyU2FRegistrationRequest.fromJson(request as any);
|
||||||
|
|
||||||
return verifyU2FRegistration(req)
|
return verifyU2FRegistration(req)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
return NextResponse.json(resp);
|
return NextResponse.json(resp);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
Prompt,
|
Prompt,
|
||||||
} from "@zitadel/proto/zitadel/oidc/v2/authorization_pb";
|
} from "@zitadel/proto/zitadel/oidc/v2/authorization_pb";
|
||||||
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
|
import { idpTypeToSlug } from "@/lib/idp";
|
||||||
|
|
||||||
async function loadSessions(ids: string[]): Promise<Session[]> {
|
async function loadSessions(ids: string[]): Promise<Session[]> {
|
||||||
const response = await listSessions(
|
const response = await listSessions(
|
||||||
@@ -168,28 +169,7 @@ export async function GET(request: NextRequest) {
|
|||||||
const host = request.nextUrl.origin;
|
const host = request.nextUrl.origin;
|
||||||
|
|
||||||
const identityProviderType = identityProviders[0].type;
|
const identityProviderType = identityProviders[0].type;
|
||||||
let provider: string;
|
let provider = idpTypeToSlug(identityProviderType);
|
||||||
|
|
||||||
switch (identityProviderType) {
|
|
||||||
case IdentityProviderType.GITHUB:
|
|
||||||
provider = "github";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.GOOGLE:
|
|
||||||
provider = "google";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.AZURE_AD:
|
|
||||||
provider = "azure";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.SAML:
|
|
||||||
provider = "saml";
|
|
||||||
break;
|
|
||||||
case IdentityProviderType.OIDC:
|
|
||||||
provider = "oidc";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
provider = "oidc";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,6 @@ export type Item = {
|
|||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum ProviderSlug {
|
|
||||||
GOOGLE = "google",
|
|
||||||
GITHUB = "github",
|
|
||||||
AZURE = "microsoft",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const demos: { name: string; items: Item[] }[] = [
|
export const demos: { name: string; items: Item[] }[] = [
|
||||||
{
|
{
|
||||||
name: "Login",
|
name: "Login",
|
||||||
|
|||||||
131
apps/login/src/lib/idp.ts
Normal file
131
apps/login/src/lib/idp.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import { AddHumanUserRequest } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||||
|
import { IDPInformation, IDPLink } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
||||||
|
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
|
import { PartialMessage } from "@zitadel/client";
|
||||||
|
|
||||||
|
// This maps the IdentityProviderType to a slug which is used in the /success and /failure routes
|
||||||
|
export function idpTypeToSlug(idpType: IdentityProviderType) {
|
||||||
|
switch (idpType) {
|
||||||
|
case IdentityProviderType.GITHUB:
|
||||||
|
return "github";
|
||||||
|
case IdentityProviderType.GOOGLE:
|
||||||
|
return "google";
|
||||||
|
case IdentityProviderType.AZURE_AD:
|
||||||
|
return "azure";
|
||||||
|
case IdentityProviderType.SAML:
|
||||||
|
return "saml";
|
||||||
|
case IdentityProviderType.OIDC:
|
||||||
|
return "oidc";
|
||||||
|
default:
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PROVIDER_MAPPING: {
|
||||||
|
[provider: string]: (
|
||||||
|
rI: IDPInformation,
|
||||||
|
) => PartialMessage<AddHumanUserRequest>;
|
||||||
|
} = {
|
||||||
|
[idpTypeToSlug(IdentityProviderType.GOOGLE)]: (idp: IDPInformation) => {
|
||||||
|
const rawInfo = idp.rawInformation?.toJson() as OIDC_USER;
|
||||||
|
console.log(rawInfo);
|
||||||
|
const idpLink: PartialMessage<IDPLink> = {
|
||||||
|
idpId: idp.idpId,
|
||||||
|
userId: idp.userId,
|
||||||
|
userName: idp.userName,
|
||||||
|
};
|
||||||
|
|
||||||
|
const req: PartialMessage<AddHumanUserRequest> = {
|
||||||
|
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: [idpLink],
|
||||||
|
};
|
||||||
|
|
||||||
|
return req;
|
||||||
|
},
|
||||||
|
[idpTypeToSlug(IdentityProviderType.AZURE_AD)]: (idp: IDPInformation) => {
|
||||||
|
const rawInfo = idp.rawInformation?.toJson() as {
|
||||||
|
jobTitle: string;
|
||||||
|
mail: string;
|
||||||
|
mobilePhone: string;
|
||||||
|
preferredLanguage: string;
|
||||||
|
id: string;
|
||||||
|
displayName?: string;
|
||||||
|
givenName?: string;
|
||||||
|
surname?: string;
|
||||||
|
officeLocation?: string;
|
||||||
|
userPrincipalName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const idpLink: PartialMessage<IDPLink> = {
|
||||||
|
idpId: idp.idpId,
|
||||||
|
userId: idp.userId,
|
||||||
|
userName: idp.userName,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(rawInfo, rawInfo.userPrincipalName);
|
||||||
|
|
||||||
|
const req: PartialMessage<AddHumanUserRequest> = {
|
||||||
|
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: [idpLink],
|
||||||
|
};
|
||||||
|
|
||||||
|
return req;
|
||||||
|
},
|
||||||
|
[idpTypeToSlug(IdentityProviderType.GITHUB)]: (idp: IDPInformation) => {
|
||||||
|
const rawInfo = idp.rawInformation?.toJson() as {
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const idpLink: PartialMessage<IDPLink> = {
|
||||||
|
idpId: idp.idpId,
|
||||||
|
userId: idp.userId,
|
||||||
|
userName: idp.userName,
|
||||||
|
};
|
||||||
|
|
||||||
|
const req: PartialMessage<AddHumanUserRequest> = {
|
||||||
|
username: idp.userName,
|
||||||
|
email: {
|
||||||
|
email: rawInfo.email,
|
||||||
|
verification: { case: "isVerified", value: true },
|
||||||
|
},
|
||||||
|
profile: {
|
||||||
|
displayName: rawInfo.name ?? "",
|
||||||
|
givenName: rawInfo.name ?? "",
|
||||||
|
familyName: rawInfo.name ?? "",
|
||||||
|
},
|
||||||
|
idpLinks: [idpLink],
|
||||||
|
};
|
||||||
|
|
||||||
|
return req;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -3,23 +3,27 @@ import {
|
|||||||
createSessionServiceClient,
|
createSessionServiceClient,
|
||||||
createSettingsServiceClient,
|
createSettingsServiceClient,
|
||||||
createUserServiceClient,
|
createUserServiceClient,
|
||||||
|
createIdpServiceClient,
|
||||||
makeReqCtx,
|
makeReqCtx,
|
||||||
} from "@zitadel/client/v2";
|
} from "@zitadel/client/v2";
|
||||||
import { createManagementServiceClient } from "@zitadel/client/v1";
|
import { createManagementServiceClient } from "@zitadel/client/v1";
|
||||||
import { createServerTransport } from "@zitadel/node";
|
import { createServerTransport } from "@zitadel/node";
|
||||||
import { GetActiveIdentityProvidersRequest } from "@zitadel/proto/zitadel/settings/v2/settings_service_pb";
|
|
||||||
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||||
import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
|
import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
|
||||||
import {
|
import {
|
||||||
RetrieveIdentityProviderIntentRequest,
|
RetrieveIdentityProviderIntentRequest,
|
||||||
|
VerifyPasskeyRegistrationRequest,
|
||||||
VerifyU2FRegistrationRequest,
|
VerifyU2FRegistrationRequest,
|
||||||
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||||
|
import { IDPInformation } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
||||||
|
|
||||||
import { CreateCallbackRequest } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
|
import { CreateCallbackRequest } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
|
||||||
import { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
import { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
||||||
import type { RedirectURLs } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
import type { RedirectURLs } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
||||||
import { ProviderSlug } from "./demos";
|
import { PartialMessage, PlainMessage } from "@zitadel/client";
|
||||||
import { PlainMessage } from "@zitadel/client";
|
import { SearchQuery as UserSearchQuery } from "@zitadel/proto/zitadel/user/v2/query_pb";
|
||||||
|
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
|
import { PROVIDER_MAPPING } from "./idp";
|
||||||
|
|
||||||
const SESSION_LIFETIME_S = 3000;
|
const SESSION_LIFETIME_S = 3000;
|
||||||
|
|
||||||
@@ -35,6 +39,8 @@ export const sessionService = createSessionServiceClient(transport);
|
|||||||
export const managementService = createManagementServiceClient(transport);
|
export const managementService = createManagementServiceClient(transport);
|
||||||
export const userService = createUserServiceClient(transport);
|
export const userService = createUserServiceClient(transport);
|
||||||
export const oidcService = createOIDCServiceClient(transport);
|
export const oidcService = createOIDCServiceClient(transport);
|
||||||
|
export const idpService = createIdpServiceClient(transport);
|
||||||
|
|
||||||
export const settingsService = createSettingsServiceClient(transport);
|
export const settingsService = createSettingsServiceClient(transport);
|
||||||
|
|
||||||
export async function getBrandingSettings(organization?: string) {
|
export async function getBrandingSettings(organization?: string) {
|
||||||
@@ -233,12 +239,19 @@ export async function getUserByID(userId: string) {
|
|||||||
return userService.getUserByID({ userId }, {});
|
return userService.getUserByID({ userId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listUsers(userName: string, organizationId: string) {
|
export async function listUsers({
|
||||||
return userService.listUsers(
|
userName,
|
||||||
{
|
email,
|
||||||
queries: organizationId
|
organizationId,
|
||||||
? [
|
}: {
|
||||||
{
|
userName?: string;
|
||||||
|
email?: string;
|
||||||
|
organizationId?: string;
|
||||||
|
}) {
|
||||||
|
const queries: PartialMessage<UserSearchQuery>[] = [];
|
||||||
|
|
||||||
|
if (userName) {
|
||||||
|
queries.push({
|
||||||
query: {
|
query: {
|
||||||
case: "userNameQuery",
|
case: "userNameQuery",
|
||||||
value: {
|
value: {
|
||||||
@@ -246,27 +259,34 @@ export async function listUsers(userName: string, organizationId: string) {
|
|||||||
method: TextQueryMethod.EQUALS,
|
method: TextQueryMethod.EQUALS,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
{
|
}
|
||||||
|
|
||||||
|
if (organizationId) {
|
||||||
|
queries.push({
|
||||||
query: {
|
query: {
|
||||||
case: "organizationIdQuery",
|
case: "organizationIdQuery",
|
||||||
value: {
|
value: {
|
||||||
organizationId,
|
organizationId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
]
|
}
|
||||||
: [
|
|
||||||
{
|
if (email) {
|
||||||
|
queries.push({
|
||||||
query: {
|
query: {
|
||||||
case: "userNameQuery",
|
case: "emailQuery",
|
||||||
value: {
|
value: {
|
||||||
userName,
|
emailAddress: email,
|
||||||
method: TextQueryMethod.EQUALS,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
],
|
}
|
||||||
|
|
||||||
|
return userService.listUsers(
|
||||||
|
{
|
||||||
|
queries: queries,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
@@ -276,14 +296,6 @@ export async function getOrgByDomain(domain: string) {
|
|||||||
return managementService.getOrgByDomainGlobal({ domain }, {});
|
return managementService.getOrgByDomainGlobal({ domain }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PROVIDER_NAME_MAPPING: {
|
|
||||||
[provider: string]: string;
|
|
||||||
} = {
|
|
||||||
[ProviderSlug.GOOGLE]: "Google",
|
|
||||||
[ProviderSlug.GITHUB]: "GitHub",
|
|
||||||
[ProviderSlug.AZURE]: "Microft",
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function startIdentityProviderFlow({
|
export async function startIdentityProviderFlow({
|
||||||
idpId,
|
idpId,
|
||||||
urls,
|
urls,
|
||||||
@@ -348,6 +360,44 @@ export async function resendEmailCode(userId: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function retrieveIDPIntent(id: string, token: string) {
|
||||||
|
return userService.retrieveIdentityProviderIntent(
|
||||||
|
{ idpIntentId: id, idpIntentToken: token },
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIDPByID(id: string) {
|
||||||
|
return idpService.getIDPByID({ id }, {}).then((resp) => resp.idp);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addIDPLink(
|
||||||
|
idp: {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
userName: string;
|
||||||
|
},
|
||||||
|
userId: string,
|
||||||
|
) {
|
||||||
|
return userService.addIDPLink(
|
||||||
|
{
|
||||||
|
idpLink: {
|
||||||
|
userId: idp.userId,
|
||||||
|
idpId: idp.id,
|
||||||
|
userName: idp.userName,
|
||||||
|
},
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUser(provider: string, info: IDPInformation) {
|
||||||
|
const userData = PROVIDER_MAPPING[provider](info);
|
||||||
|
console.log("ud", userData);
|
||||||
|
return userService.addHumanUser(userData, {});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
@@ -433,24 +483,11 @@ export async function getActiveIdentityProviders(orgId?: string) {
|
|||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
export async function verifyPasskeyRegistration(
|
export async function verifyPasskeyRegistration(
|
||||||
passkeyId: string,
|
request: PartialMessage<VerifyPasskeyRegistrationRequest>,
|
||||||
passkeyName: string,
|
|
||||||
publicKeyCredential:
|
|
||||||
| {
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
| undefined,
|
|
||||||
userId: string,
|
|
||||||
) {
|
) {
|
||||||
return userService.verifyPasskeyRegistration(
|
// TODO: find a better way to handle this
|
||||||
{
|
request = VerifyPasskeyRegistrationRequest.fromJson(request as any);
|
||||||
passkeyId,
|
return userService.verifyPasskeyRegistration(request, {});
|
||||||
passkeyName,
|
|
||||||
publicKeyCredential,
|
|
||||||
userId,
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ export function middleware(request: NextRequest) {
|
|||||||
requestHeaders.set("x-zitadel-login-client", SERVICE_USER_ID);
|
requestHeaders.set("x-zitadel-login-client", SERVICE_USER_ID);
|
||||||
|
|
||||||
// this is a workaround for the next.js server not forwarding the host header
|
// this is a workaround for the next.js server not forwarding the host header
|
||||||
requestHeaders.set("x-zitadel-forwarded", `host="${request.nextUrl.host}"`);
|
// requestHeaders.set("x-zitadel-forwarded", `host="${request.nextUrl.host}"`);
|
||||||
// requestHeaders.set("x-zitadel-public-host", `${request.nextUrl.host}`);
|
requestHeaders.set("x-zitadel-public-host", `${request.nextUrl.host}`);
|
||||||
|
|
||||||
// this is a workaround for the next.js server not forwarding the host header
|
// this is a workaround for the next.js server not forwarding the host header
|
||||||
// requestHeaders.set("x-zitadel-instance-host", `${INSTANCE}`);
|
requestHeaders.set(
|
||||||
|
"x-zitadel-instance-host",
|
||||||
|
`${INSTANCE}`.replace("https://", ""),
|
||||||
|
);
|
||||||
|
|
||||||
const responseHeaders = new Headers();
|
const responseHeaders = new Headers();
|
||||||
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import Alert from "./Alert";
|
|||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import BackButton from "./BackButton";
|
import BackButton from "./BackButton";
|
||||||
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||||
|
import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
|
||||||
|
|
||||||
// either loginName or sessionId must be provided
|
// either loginName or sessionId must be provided
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -78,7 +79,7 @@ export default function LoginPasskey({
|
|||||||
loginName,
|
loginName,
|
||||||
sessionId,
|
sessionId,
|
||||||
organization,
|
organization,
|
||||||
challenges: {
|
challenges: RequestChallenges.fromJson({
|
||||||
webAuthN: {
|
webAuthN: {
|
||||||
domain: "",
|
domain: "",
|
||||||
// USER_VERIFICATION_REQUIREMENT_UNSPECIFIED = 0;
|
// USER_VERIFICATION_REQUIREMENT_UNSPECIFIED = 0;
|
||||||
@@ -87,7 +88,7 @@ export default function LoginPasskey({
|
|||||||
// USER_VERIFICATION_REQUIREMENT_DISCOURAGED = 3; - mfa
|
// USER_VERIFICATION_REQUIREMENT_DISCOURAGED = 3; - mfa
|
||||||
userVerificationRequirement: userVerificationRequirement,
|
userVerificationRequirement: userVerificationRequirement,
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
authRequestId,
|
authRequestId,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export default function RegisterPasskey({
|
|||||||
return submitRegister().then((resp: RegisterPasskeyResponse) => {
|
return submitRegister().then((resp: RegisterPasskeyResponse) => {
|
||||||
const passkeyId = resp.passkeyId;
|
const passkeyId = resp.passkeyId;
|
||||||
const options: CredentialCreationOptions =
|
const options: CredentialCreationOptions =
|
||||||
(resp.publicKeyCredentialCreationOptions?.toJson() as CredentialCreationOptions) ??
|
(resp.publicKeyCredentialCreationOptions as CredentialCreationOptions) ??
|
||||||
{};
|
{};
|
||||||
|
|
||||||
if (options?.publicKey) {
|
if (options?.publicKey) {
|
||||||
@@ -143,6 +143,7 @@ export default function RegisterPasskey({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return submitVerify(passkeyId, "", data, sessionId).then(() => {
|
return submitVerify(passkeyId, "", data, sessionId).then(() => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
@@ -194,10 +195,11 @@ export default function RegisterPasskey({
|
|||||||
type="button"
|
type="button"
|
||||||
variant={ButtonVariants.Secondary}
|
variant={ButtonVariants.Secondary}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const params = new URLSearchParams();
|
|
||||||
if (authRequestId) {
|
if (authRequestId) {
|
||||||
params.set("authRequest", authRequestId);
|
const params = new URLSearchParams({
|
||||||
}
|
authRequest: authRequestId,
|
||||||
|
});
|
||||||
|
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
params.set("sessionId", sessionId);
|
params.set("sessionId", sessionId);
|
||||||
}
|
}
|
||||||
@@ -207,6 +209,18 @@ export default function RegisterPasskey({
|
|||||||
}
|
}
|
||||||
|
|
||||||
router.push("/login?" + params);
|
router.push("/login?" + params);
|
||||||
|
} else {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
if (sessionId) {
|
||||||
|
params.append("sessionId", sessionId);
|
||||||
|
}
|
||||||
|
if (organization) {
|
||||||
|
params.append("organization", organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push("/signedin?" + params);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
skip
|
skip
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export default function RegisterU2F({
|
|||||||
return submitRegister().then((resp: RegisterU2FResponse) => {
|
return submitRegister().then((resp: RegisterU2FResponse) => {
|
||||||
const u2fId = resp.u2fId;
|
const u2fId = resp.u2fId;
|
||||||
const options: CredentialCreationOptions =
|
const options: CredentialCreationOptions =
|
||||||
(resp.publicKeyCredentialCreationOptions?.toJson() as CredentialCreationOptions) ??
|
(resp.publicKeyCredentialCreationOptions as CredentialCreationOptions) ??
|
||||||
{};
|
{};
|
||||||
|
|
||||||
if (options.publicKey) {
|
if (options.publicKey) {
|
||||||
|
|||||||
@@ -121,11 +121,12 @@ export default function SessionItem({
|
|||||||
|
|
||||||
<XCircleIcon
|
<XCircleIcon
|
||||||
className="hidden group-hover:block h-5 w-5 transition-all opacity-50 hover:opacity-100"
|
className="hidden group-hover:block h-5 w-5 transition-all opacity-50 hover:opacity-100"
|
||||||
onClick={() =>
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
clearSession(session.id).then(() => {
|
clearSession(session.id).then(() => {
|
||||||
reload();
|
reload();
|
||||||
})
|
});
|
||||||
}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { ReactNode, useState } from "react";
|
|
||||||
|
|
||||||
|
import { ReactNode, useState } from "react";
|
||||||
import {
|
import {
|
||||||
SignInWithGitlab,
|
SignInWithGitlab,
|
||||||
SignInWithAzureAD,
|
SignInWithAzureAD,
|
||||||
@@ -8,10 +8,10 @@ import {
|
|||||||
SignInWithGithub,
|
SignInWithGithub,
|
||||||
} from "@zitadel/react";
|
} from "@zitadel/react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { ProviderSlug } from "@/lib/demos";
|
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
import BackButton from "./BackButton";
|
|
||||||
import { IdentityProvider } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
import { IdentityProvider } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
|
import { idpTypeToSlug } from "@/lib/idp";
|
||||||
|
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
|
|
||||||
export interface SignInWithIDPProps {
|
export interface SignInWithIDPProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
@@ -36,7 +36,7 @@ export function SignInWithIDP({
|
|||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
async function startFlow(idpId: string, provider: ProviderSlug) {
|
async function startFlow(idpId: string, provider: string) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@@ -78,62 +78,65 @@ export function SignInWithIDP({
|
|||||||
{identityProviders &&
|
{identityProviders &&
|
||||||
identityProviders.map((idp, i) => {
|
identityProviders.map((idp, i) => {
|
||||||
switch (idp.type) {
|
switch (idp.type) {
|
||||||
case 6: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITHUB:
|
case IdentityProviderType.GITHUB:
|
||||||
return (
|
return (
|
||||||
<SignInWithGithub
|
<SignInWithGithub
|
||||||
key={`idp-${i}`}
|
key={`idp-${i}`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
startFlow(idp.id, ProviderSlug.GITHUB).then(
|
startFlow(
|
||||||
({ authUrl }) => {
|
idp.id,
|
||||||
|
idpTypeToSlug(IdentityProviderType.GITHUB),
|
||||||
|
).then(({ authUrl }) => {
|
||||||
router.push(authUrl);
|
router.push(authUrl);
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
></SignInWithGithub>
|
></SignInWithGithub>
|
||||||
);
|
);
|
||||||
case 7: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITHUB_ES:
|
case IdentityProviderType.GITHUB_ES:
|
||||||
return (
|
return (
|
||||||
<SignInWithGithub
|
<SignInWithGithub
|
||||||
key={`idp-${i}`}
|
key={`idp-${i}`}
|
||||||
onClick={() => alert("TODO: unimplemented")}
|
onClick={() => alert("TODO: unimplemented")}
|
||||||
></SignInWithGithub>
|
></SignInWithGithub>
|
||||||
);
|
);
|
||||||
case 5: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_AZURE_AD:
|
case IdentityProviderType.AZURE_AD:
|
||||||
return (
|
return (
|
||||||
<SignInWithAzureAD
|
<SignInWithAzureAD
|
||||||
key={`idp-${i}`}
|
key={`idp-${i}`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
startFlow(idp.id, ProviderSlug.AZURE).then(
|
startFlow(
|
||||||
({ authUrl }) => {
|
idp.id,
|
||||||
|
idpTypeToSlug(IdentityProviderType.AZURE_AD),
|
||||||
|
).then(({ authUrl }) => {
|
||||||
router.push(authUrl);
|
router.push(authUrl);
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
></SignInWithAzureAD>
|
></SignInWithAzureAD>
|
||||||
);
|
);
|
||||||
case 10: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GOOGLE:
|
case IdentityProviderType.GOOGLE:
|
||||||
return (
|
return (
|
||||||
<SignInWithGoogle
|
<SignInWithGoogle
|
||||||
key={`idp-${i}`}
|
key={`idp-${i}`}
|
||||||
e2e="google"
|
e2e="google"
|
||||||
name={idp.name}
|
name={idp.name}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
startFlow(idp.id, ProviderSlug.GOOGLE).then(
|
startFlow(
|
||||||
({ authUrl }) => {
|
idp.id,
|
||||||
|
idpTypeToSlug(IdentityProviderType.GOOGLE),
|
||||||
|
).then(({ authUrl }) => {
|
||||||
router.push(authUrl);
|
router.push(authUrl);
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
></SignInWithGoogle>
|
></SignInWithGoogle>
|
||||||
);
|
);
|
||||||
case 8: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITLAB:
|
case IdentityProviderType.GITLAB:
|
||||||
return (
|
return (
|
||||||
<SignInWithGitlab
|
<SignInWithGitlab
|
||||||
key={`idp-${i}`}
|
key={`idp-${i}`}
|
||||||
onClick={() => alert("TODO: unimplemented")}
|
onClick={() => alert("TODO: unimplemented")}
|
||||||
></SignInWithGitlab>
|
></SignInWithGitlab>
|
||||||
);
|
);
|
||||||
case 9: //IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITLAB_SELF_HOSTED:
|
case IdentityProviderType.GITLAB_SELF_HOSTED:
|
||||||
return (
|
return (
|
||||||
<SignInWithGitlab
|
<SignInWithGitlab
|
||||||
key={`idp-${i}`}
|
key={`idp-${i}`}
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ export default function UserAvatar({
|
|||||||
loginName={loginName ?? ""}
|
loginName={loginName ?? ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="ml-4 text-14px">{loginName}</span>
|
<span className="ml-4 text-14px max-w-[250px] text-ellipsis overflow-hidden">
|
||||||
|
{loginName}
|
||||||
|
</span>
|
||||||
<span className="flex-grow"></span>
|
<span className="flex-grow"></span>
|
||||||
{showDropdown && (
|
{showDropdown && (
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export async function createSessionAndUpdateCookie(
|
|||||||
? {
|
? {
|
||||||
user: { search: { case: "loginName", value: loginName } },
|
user: { search: { case: "loginName", value: loginName } },
|
||||||
password: { password },
|
password: { password },
|
||||||
// totp: { code: totpCode },
|
|
||||||
}
|
}
|
||||||
: { user: { search: { case: "loginName", value: loginName } } },
|
: { user: { search: { case: "loginName", value: loginName } } },
|
||||||
challenges,
|
challenges,
|
||||||
@@ -50,7 +49,7 @@ export async function createSessionAndUpdateCookie(
|
|||||||
createdSession.sessionToken,
|
createdSession.sessionToken,
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
if (response?.session && response.session?.factors?.user?.loginName) {
|
if (response?.session && response.session?.factors?.user?.loginName) {
|
||||||
const sessionCookie: any = {
|
const sessionCookie: CustomCookieData = {
|
||||||
id: createdSession.sessionId,
|
id: createdSession.sessionId,
|
||||||
token: createdSession.sessionToken,
|
token: createdSession.sessionToken,
|
||||||
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||||
@@ -103,7 +102,7 @@ export async function createSessionForUserIdAndUpdateCookie(
|
|||||||
createdSession.sessionToken,
|
createdSession.sessionToken,
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
if (response?.session && response.session?.factors?.user?.loginName) {
|
if (response?.session && response.session?.factors?.user?.loginName) {
|
||||||
const sessionCookie: any = {
|
const sessionCookie: CustomCookieData = {
|
||||||
id: createdSession.sessionId,
|
id: createdSession.sessionId,
|
||||||
token: createdSession.sessionToken,
|
token: createdSession.sessionToken,
|
||||||
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||||
@@ -153,7 +152,7 @@ export async function createSessionForIdpAndUpdateCookie(
|
|||||||
createdSession.sessionToken,
|
createdSession.sessionToken,
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
if (response?.session && response.session?.factors?.user?.loginName) {
|
if (response?.session && response.session?.factors?.user?.loginName) {
|
||||||
const sessionCookie: any = {
|
const sessionCookie: CustomCookieData = {
|
||||||
id: createdSession.sessionId,
|
id: createdSession.sessionId,
|
||||||
token: createdSession.sessionToken,
|
token: createdSession.sessionToken,
|
||||||
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { FeatureService } from "@zitadel/proto/zitadel/feature/v2/feature_service_connect";
|
import { FeatureService } from "@zitadel/proto/zitadel/feature/v2/feature_service_connect";
|
||||||
|
import { IdentityProviderService } from "@zitadel/proto/zitadel/idp/v2/idp_service_connect";
|
||||||
import { RequestContext } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
import { RequestContext } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
||||||
import { OIDCService } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_connect";
|
import { OIDCService } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_connect";
|
||||||
import { OrganizationService } from "@zitadel/proto/zitadel/org/v2/org_service_connect";
|
import { OrganizationService } from "@zitadel/proto/zitadel/org/v2/org_service_connect";
|
||||||
import { SessionService } from "@zitadel/proto/zitadel/session/v2/session_service_connect";
|
import { SessionService } from "@zitadel/proto/zitadel/session/v2/session_service_connect";
|
||||||
import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_connect";
|
import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_connect";
|
||||||
import { UserService } from "@zitadel/proto/zitadel/user/v2/user_service_connect";
|
import { UserService } from "@zitadel/proto/zitadel/user/v2/user_service_connect";
|
||||||
|
|
||||||
import { createClientFor } from "./helpers";
|
import { createClientFor } from "./helpers";
|
||||||
|
|
||||||
export const createUserServiceClient = createClientFor(UserService);
|
export const createUserServiceClient = createClientFor(UserService);
|
||||||
@@ -13,6 +15,7 @@ export const createSessionServiceClient = createClientFor(SessionService);
|
|||||||
export const createOIDCServiceClient = createClientFor(OIDCService);
|
export const createOIDCServiceClient = createClientFor(OIDCService);
|
||||||
export const createOrganizationServiceClient = createClientFor(OrganizationService);
|
export const createOrganizationServiceClient = createClientFor(OrganizationService);
|
||||||
export const createFeatureServiceClient = createClientFor(FeatureService);
|
export const createFeatureServiceClient = createClientFor(FeatureService);
|
||||||
|
export const createIdpServiceClient = createClientFor(IdentityProviderService);
|
||||||
|
|
||||||
export function makeReqCtx(orgId: string | undefined): Partial<RequestContext> {
|
export function makeReqCtx(orgId: string | undefined): Partial<RequestContext> {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,39 +2,20 @@
|
|||||||
|
|
||||||
import { ReactNode, forwardRef } from "react";
|
import { ReactNode, forwardRef } from "react";
|
||||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||||
|
import { IdpButtonClasses } from "./classes";
|
||||||
|
|
||||||
export const SignInWithAzureAD = forwardRef<
|
export const SignInWithAzureAD = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||||
HTMLButtonElement,
|
|
||||||
SignInWithIdentityProviderProps
|
|
||||||
>(
|
|
||||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||||
<button
|
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||||
type="button"
|
|
||||||
ref={ref}
|
|
||||||
className={`ztdl-w-full ztdl-cursor-pointer ztdl-flex ztdl-flex-row ztdl-items-center ztdl-bg-white ztdl-text-black dark:ztdl-bg-transparent dark:ztdl-text-white border ztdl-border-divider-light dark:ztdl-border-divider-dark rounded-md px-4 text-sm ${className}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div className="ztdl-h-12 ztdl-p-[10px] ztdl-w-12 ztdl-flex ztdl-items-center ztdl-justify-center">
|
<div className="ztdl-h-12 ztdl-p-[10px] ztdl-w-12 ztdl-flex ztdl-items-center ztdl-justify-center">
|
||||||
<svg
|
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21" className="w-full h-full">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="21"
|
|
||||||
height="21"
|
|
||||||
viewBox="0 0 21 21"
|
|
||||||
className="w-full h-full"
|
|
||||||
>
|
|
||||||
<path fill="#f25022" d="M1 1H10V10H1z"></path>
|
<path fill="#f25022" d="M1 1H10V10H1z"></path>
|
||||||
<path fill="#00a4ef" d="M1 11H10V20H1z"></path>
|
<path fill="#00a4ef" d="M1 11H10V20H1z"></path>
|
||||||
<path fill="#7fba00" d="M11 1H20V10H11z"></path>
|
<path fill="#7fba00" d="M11 1H20V10H11z"></path>
|
||||||
<path fill="#ffb900" d="M11 11H20V20H11z"></path>
|
<path fill="#ffb900" d="M11 11H20V20H11z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
{children ? (
|
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with AzureAD"}</span>}
|
||||||
children
|
|
||||||
) : (
|
|
||||||
<span className="ztdl-ml-4">
|
|
||||||
{name ? name : "Sign in with AzureAD"}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,18 +2,11 @@
|
|||||||
|
|
||||||
import { ReactNode, forwardRef } from "react";
|
import { ReactNode, forwardRef } from "react";
|
||||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||||
|
import { IdpButtonClasses } from "./classes";
|
||||||
|
|
||||||
export const SignInWithGithub = forwardRef<
|
export const SignInWithGithub = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||||
HTMLButtonElement,
|
|
||||||
SignInWithIdentityProviderProps
|
|
||||||
>(
|
|
||||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||||
<button
|
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||||
type="button"
|
|
||||||
ref={ref}
|
|
||||||
className={`ztdl-h-[50px] ztdl-w-full ztdl-cursor-pointer ztdl-flex ztdl-flex-row ztdl-items-center ztdl-bg-white ztdl-text-black dark:ztdl-bg-transparent dark:ztdl-text-white border ztdl-border-divider-light dark:ztdl-border-divider-dark rounded-md px-4 text-sm ${className}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div className="ztdl-h-8 ztdl-w-8 ztdl-mx-2 flex items-center justify-center">
|
<div className="ztdl-h-8 ztdl-w-8 ztdl-mx-2 flex items-center justify-center">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@@ -46,11 +39,7 @@ export const SignInWithGithub = forwardRef<
|
|||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
{children ? (
|
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with GitHub"}</span>}
|
||||||
children
|
|
||||||
) : (
|
|
||||||
<span className="ztdl-ml-4">{name ? name : "Sign in with GitHub"}</span>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
import { ReactNode, forwardRef } from "react";
|
import { ReactNode, forwardRef } from "react";
|
||||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||||
|
import { IdpButtonClasses } from "./classes";
|
||||||
|
|
||||||
export const SignInWithGitlab = forwardRef<
|
export const SignInWithGitlab = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||||
HTMLButtonElement,
|
|
||||||
SignInWithIdentityProviderProps
|
|
||||||
>(
|
|
||||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||||
<button
|
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||||
type="button"
|
|
||||||
ref={ref}
|
|
||||||
className={`ztdl-w-full ztdl-cursor-pointer ztdl-flex ztdl-flex-row ztdl-items-center ztdl-bg-white ztdl-text-black dark:ztdl-bg-transparent dark:ztdl-text-white border ztdl-border-divider-light dark:ztdl-border-divider-dark rounded-md px-4 text-sm ${className}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div className="ztdl-h-12 ztdl-w-12 flex items-center justify-center">
|
<div className="ztdl-h-12 ztdl-w-12 flex items-center justify-center">
|
||||||
<svg
|
<svg xmlns="http://www.w3.org/2000/svg" width={25} height={24} fill="none">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width={25}
|
|
||||||
height={24}
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fill="#e24329"
|
fill="#e24329"
|
||||||
d="m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5z"
|
d="m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5z"
|
||||||
@@ -37,11 +25,7 @@ export const SignInWithGitlab = forwardRef<
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
{children ? (
|
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with GitLab"}</span>}
|
||||||
children
|
|
||||||
) : (
|
|
||||||
<span className="ztdl-ml-4">{name ? name : "Sign in with GitLab"}</span>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
import { ReactNode, forwardRef } from "react";
|
import { ReactNode, forwardRef } from "react";
|
||||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||||
|
import { IdpButtonClasses } from "./classes";
|
||||||
|
|
||||||
export const SignInWithGoogle = forwardRef<
|
export const SignInWithGoogle = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||||
HTMLButtonElement,
|
|
||||||
SignInWithIdentityProviderProps
|
|
||||||
>(
|
|
||||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||||
<button
|
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||||
type="button"
|
|
||||||
ref={ref}
|
|
||||||
className={`ztdl-w-full ztdl-cursor-pointer ztdl-flex ztdl-flex-row ztdl-items-center ztdl-bg-white ztdl-text-black dark:ztdl-bg-transparent dark:ztdl-text-white border ztdl-border-divider-light dark:ztdl-border-divider-dark rounded-md px-4 text-sm ${className}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div className="ztdl-h-12 ztdl-w-12 ztdl-flex ztdl-items-center ztdl-justify-center">
|
<div className="ztdl-h-12 ztdl-w-12 ztdl-flex ztdl-items-center ztdl-justify-center">
|
||||||
<svg
|
<svg xmlns="http://www.w3.org/2000/svg" xmlSpace="preserve" id="Capa_1" viewBox="0 0 150 150">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlSpace="preserve"
|
|
||||||
id="Capa_1"
|
|
||||||
viewBox="0 0 150 150"
|
|
||||||
>
|
|
||||||
<style>
|
<style>
|
||||||
{
|
{
|
||||||
".st0{fill:#1a73e8}.st1{fill:#ea4335}.st2{fill:#4285f4}.st3{fill:#fbbc04}.st4{fill:#34a853}.st5{fill:#4caf50}.st6{fill:#1e88e5}.st7{fill:#e53935}.st8{fill:#c62828}.st9{fill:#fbc02d}.st10{fill:#1565c0}.st11{fill:#2e7d32}.st16{clip-path:url(#SVGID_2_)}.st17{fill:#188038}.st18,.st19{opacity:.2;fill:#fff;enable-background:new}.st19{opacity:.3;fill:#0d652d}.st20{clip-path:url(#SVGID_4_)}.st21{opacity:.3;fill:url(#_45_shadow_1_);enable-background:new}.st22{clip-path:url(#SVGID_6_)}.st23{fill:#fa7b17}.st24,.st25,.st26{opacity:.3;fill:#174ea6;enable-background:new}.st25,.st26{fill:#a50e0e}.st26{fill:#e37400}.st27{fill:url(#Finish_mask_1_)}.st28{fill:#fff}.st29{fill:#0c9d58}.st30,.st31{opacity:.2;fill:#004d40;enable-background:new}.st31{fill:#3e2723}.st32{fill:#ffc107}.st33{fill:#1a237e;enable-background:new}.st33,.st34{opacity:.2}.st35{fill:#1a237e}.st36{fill:url(#SVGID_7_)}.st37{fill:#fbbc05}.st38{clip-path:url(#SVGID_9_);fill:#e53935}.st39{clip-path:url(#SVGID_11_);fill:#fbc02d}.st40{clip-path:url(#SVGID_13_);fill:#e53935}.st41{clip-path:url(#SVGID_15_);fill:#fbc02d}"
|
".st0{fill:#1a73e8}.st1{fill:#ea4335}.st2{fill:#4285f4}.st3{fill:#fbbc04}.st4{fill:#34a853}.st5{fill:#4caf50}.st6{fill:#1e88e5}.st7{fill:#e53935}.st8{fill:#c62828}.st9{fill:#fbc02d}.st10{fill:#1565c0}.st11{fill:#2e7d32}.st16{clip-path:url(#SVGID_2_)}.st17{fill:#188038}.st18,.st19{opacity:.2;fill:#fff;enable-background:new}.st19{opacity:.3;fill:#0d652d}.st20{clip-path:url(#SVGID_4_)}.st21{opacity:.3;fill:url(#_45_shadow_1_);enable-background:new}.st22{clip-path:url(#SVGID_6_)}.st23{fill:#fa7b17}.st24,.st25,.st26{opacity:.3;fill:#174ea6;enable-background:new}.st25,.st26{fill:#a50e0e}.st26{fill:#e37400}.st27{fill:url(#Finish_mask_1_)}.st28{fill:#fff}.st29{fill:#0c9d58}.st30,.st31{opacity:.2;fill:#004d40;enable-background:new}.st31{fill:#3e2723}.st32{fill:#ffc107}.st33{fill:#1a237e;enable-background:new}.st33,.st34{opacity:.2}.st35{fill:#1a237e}.st36{fill:url(#SVGID_7_)}.st37{fill:#fbbc05}.st38{clip-path:url(#SVGID_9_);fill:#e53935}.st39{clip-path:url(#SVGID_11_);fill:#fbc02d}.st40{clip-path:url(#SVGID_13_);fill:#e53935}.st41{clip-path:url(#SVGID_15_);fill:#fbc02d}"
|
||||||
@@ -50,11 +38,7 @@ export const SignInWithGoogle = forwardRef<
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
{children ? (
|
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with Google"}</span>}
|
||||||
children
|
|
||||||
) : (
|
|
||||||
<span className="ztdl-ml-4">{name ? name : "Sign in with Google"}</span>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
2
packages/zitadel-react/src/components/classes.ts
Normal file
2
packages/zitadel-react/src/components/classes.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export const IdpButtonClasses =
|
||||||
|
"transition-all ztdl-w-full ztdl-cursor-pointer ztdl-flex ztdl-flex-row ztdl-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";
|
||||||
Reference in New Issue
Block a user