mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-11 21:22:14 +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, PROVIDER_NAME_MAPPING } from "@/lib/zitadel";
|
||||
import { getBrandingSettings } from "@/lib/zitadel";
|
||||
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({
|
||||
searchParams,
|
||||
params,
|
||||
}: {
|
||||
searchParams: Record<string | number | symbol, string | undefined>;
|
||||
params: { provider: ProviderSlug };
|
||||
params: { provider: string };
|
||||
}) {
|
||||
const { id, token, authRequestId, organization } = searchParams;
|
||||
const { provider } = params;
|
||||
|
||||
@@ -1,174 +1,96 @@
|
||||
import { ProviderSlug } from "@/lib/demos";
|
||||
import { getBrandingSettings, userService } from "@/lib/zitadel";
|
||||
import { PROVIDER_MAPPING } from "@/lib/idp";
|
||||
import {
|
||||
addIDPLink,
|
||||
createUser,
|
||||
getBrandingSettings,
|
||||
getIDPByID,
|
||||
listUsers,
|
||||
retrieveIDPIntent,
|
||||
} from "@/lib/zitadel";
|
||||
import Alert, { AlertType } from "@/ui/Alert";
|
||||
import DynamicTheme from "@/ui/DynamicTheme";
|
||||
import IdpSignin from "@/ui/IdpSignin";
|
||||
import { AddHumanUserRequest } from "@zitadel/proto/zitadel/user/v2/user_service_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);
|
||||
}
|
||||
import { AutoLinkingOption } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
|
||||
|
||||
export default async function Page({
|
||||
searchParams,
|
||||
params,
|
||||
}: {
|
||||
searchParams: Record<string | number | symbol, string | undefined>;
|
||||
params: { provider: ProviderSlug };
|
||||
params: { provider: string };
|
||||
}) {
|
||||
const { id, token, authRequestId, organization } = searchParams;
|
||||
const { provider } = params;
|
||||
|
||||
const branding = await getBrandingSettings(organization);
|
||||
|
||||
if (provider && id && token) {
|
||||
return retrieveIDPIntent(id, token)
|
||||
.then((resp) => {
|
||||
.then(async (resp) => {
|
||||
const { idpInformation, userId } = resp;
|
||||
|
||||
if (idpInformation) {
|
||||
// handle login
|
||||
if (userId) {
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Login successful</h1>
|
||||
<div>You have successfully been loggedIn!</div>
|
||||
if (userId) {
|
||||
// TODO: update user if idp.options.isAutoUpdate is true
|
||||
|
||||
<IdpSignin
|
||||
userId={userId}
|
||||
idpIntent={{ idpIntentId: id, idpIntentToken: token }}
|
||||
authRequestId={authRequestId}
|
||||
/>
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
} else {
|
||||
// handle register
|
||||
return createUser(provider, idpInformation)
|
||||
.then((userId) => {
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Login successful</h1>
|
||||
<div>You have successfully been loggedIn!</div>
|
||||
|
||||
<IdpSignin
|
||||
userId={userId}
|
||||
idpIntent={{ idpIntentId: id, idpIntentToken: token }}
|
||||
authRequestId={authRequestId}
|
||||
/>
|
||||
</div>
|
||||
</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 {
|
||||
foundUser = await listUsers({
|
||||
userName: idpInformation.userName,
|
||||
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 (
|
||||
<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>
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register failed</h1>
|
||||
<h1>Linking failed</h1>
|
||||
<div className="w-full">
|
||||
{
|
||||
<Alert type={AlertType.ALERT}>
|
||||
@@ -180,9 +102,66 @@ export default async function Page({
|
||||
</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 {
|
||||
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) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProviderSlug } from "@/lib/demos";
|
||||
import { idpTypeToSlug } from "@/lib/idp";
|
||||
import {
|
||||
getActiveIdentityProviders,
|
||||
getLoginSettings,
|
||||
@@ -7,14 +7,16 @@ import {
|
||||
startIdentityProviderFlow,
|
||||
} from "@/lib/zitadel";
|
||||
import { createSessionForUserIdAndUpdateCookie } from "@/utils/session";
|
||||
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
if (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) {
|
||||
const userId = users.result[0].userId;
|
||||
return createSessionForUserIdAndUpdateCookie(
|
||||
@@ -64,28 +66,8 @@ export async function POST(request: NextRequest) {
|
||||
const host = request.nextUrl.origin;
|
||||
|
||||
const identityProviderType = identityProviders[0].type;
|
||||
let provider: string;
|
||||
|
||||
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 provider = idpTypeToSlug(identityProviderType);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
|
||||
@@ -18,14 +18,19 @@ export async function POST(request: NextRequest) {
|
||||
const session = await getSession(sessionCookie.id, sessionCookie.token);
|
||||
|
||||
const userId = session?.session?.factors?.user?.id;
|
||||
|
||||
console.log("payload", {
|
||||
passkeyId,
|
||||
passkeyName,
|
||||
publicKeyCredential,
|
||||
userId,
|
||||
});
|
||||
if (userId) {
|
||||
return verifyPasskeyRegistration(
|
||||
return verifyPasskeyRegistration({
|
||||
passkeyId,
|
||||
passkeyName,
|
||||
publicKeyCredential,
|
||||
userId,
|
||||
)
|
||||
})
|
||||
.then((resp) => {
|
||||
return NextResponse.json(resp);
|
||||
})
|
||||
|
||||
@@ -5,7 +5,10 @@ export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
if (body) {
|
||||
const { loginName, organization } = body;
|
||||
return listUsers(loginName, organization).then((users) => {
|
||||
return listUsers({
|
||||
userName: loginName,
|
||||
organizationId: organization,
|
||||
}).then((users) => {
|
||||
if (
|
||||
users.details &&
|
||||
Number(users.details.totalResult) == 1 &&
|
||||
|
||||
@@ -22,12 +22,15 @@ export async function POST(request: NextRequest) {
|
||||
const userId = session?.session?.factors?.user?.id;
|
||||
|
||||
if (userId) {
|
||||
const req: PlainMessage<VerifyU2FRegistrationRequest> = {
|
||||
let req: PlainMessage<VerifyU2FRegistrationRequest> = {
|
||||
publicKeyCredential,
|
||||
u2fId,
|
||||
userId,
|
||||
tokenName: passkeyName,
|
||||
};
|
||||
|
||||
req = VerifyU2FRegistrationRequest.fromJson(request as any);
|
||||
|
||||
return verifyU2FRegistration(req)
|
||||
.then((resp) => {
|
||||
return NextResponse.json(resp);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
Prompt,
|
||||
} from "@zitadel/proto/zitadel/oidc/v2/authorization_pb";
|
||||
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { idpTypeToSlug } from "@/lib/idp";
|
||||
|
||||
async function loadSessions(ids: string[]): Promise<Session[]> {
|
||||
const response = await listSessions(
|
||||
@@ -168,28 +169,7 @@ export async function GET(request: NextRequest) {
|
||||
const host = request.nextUrl.origin;
|
||||
|
||||
const identityProviderType = identityProviders[0].type;
|
||||
let provider: string;
|
||||
|
||||
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;
|
||||
}
|
||||
let provider = idpTypeToSlug(identityProviderType);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
|
||||
@@ -4,12 +4,6 @@ export type Item = {
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export enum ProviderSlug {
|
||||
GOOGLE = "google",
|
||||
GITHUB = "github",
|
||||
AZURE = "microsoft",
|
||||
}
|
||||
|
||||
export const demos: { name: string; items: Item[] }[] = [
|
||||
{
|
||||
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,
|
||||
createSettingsServiceClient,
|
||||
createUserServiceClient,
|
||||
createIdpServiceClient,
|
||||
makeReqCtx,
|
||||
} from "@zitadel/client/v2";
|
||||
import { createManagementServiceClient } from "@zitadel/client/v1";
|
||||
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 { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
|
||||
import {
|
||||
RetrieveIdentityProviderIntentRequest,
|
||||
VerifyPasskeyRegistrationRequest,
|
||||
VerifyU2FRegistrationRequest,
|
||||
} 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 { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2/object_pb";
|
||||
import type { RedirectURLs } from "@zitadel/proto/zitadel/user/v2/idp_pb";
|
||||
import { ProviderSlug } from "./demos";
|
||||
import { PlainMessage } from "@zitadel/client";
|
||||
import { PartialMessage, 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;
|
||||
|
||||
@@ -35,6 +39,8 @@ export const sessionService = createSessionServiceClient(transport);
|
||||
export const managementService = createManagementServiceClient(transport);
|
||||
export const userService = createUserServiceClient(transport);
|
||||
export const oidcService = createOIDCServiceClient(transport);
|
||||
export const idpService = createIdpServiceClient(transport);
|
||||
|
||||
export const settingsService = createSettingsServiceClient(transport);
|
||||
|
||||
export async function getBrandingSettings(organization?: string) {
|
||||
@@ -233,40 +239,54 @@ export async function getUserByID(userId: string) {
|
||||
return userService.getUserByID({ userId }, {});
|
||||
}
|
||||
|
||||
export async function listUsers(userName: string, organizationId: string) {
|
||||
export async function listUsers({
|
||||
userName,
|
||||
email,
|
||||
organizationId,
|
||||
}: {
|
||||
userName?: string;
|
||||
email?: string;
|
||||
organizationId?: string;
|
||||
}) {
|
||||
const queries: PartialMessage<UserSearchQuery>[] = [];
|
||||
|
||||
if (userName) {
|
||||
queries.push({
|
||||
query: {
|
||||
case: "userNameQuery",
|
||||
value: {
|
||||
userName,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (organizationId) {
|
||||
queries.push({
|
||||
query: {
|
||||
case: "organizationIdQuery",
|
||||
value: {
|
||||
organizationId,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (email) {
|
||||
queries.push({
|
||||
query: {
|
||||
case: "emailQuery",
|
||||
value: {
|
||||
emailAddress: email,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return userService.listUsers(
|
||||
{
|
||||
queries: organizationId
|
||||
? [
|
||||
{
|
||||
query: {
|
||||
case: "userNameQuery",
|
||||
value: {
|
||||
userName,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
query: {
|
||||
case: "organizationIdQuery",
|
||||
value: {
|
||||
organizationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
query: {
|
||||
case: "userNameQuery",
|
||||
value: {
|
||||
userName,
|
||||
method: TextQueryMethod.EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
queries: queries,
|
||||
},
|
||||
{},
|
||||
);
|
||||
@@ -276,14 +296,6 @@ export async function getOrgByDomain(domain: string) {
|
||||
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({
|
||||
idpId,
|
||||
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
|
||||
@@ -433,24 +483,11 @@ export async function getActiveIdentityProviders(orgId?: string) {
|
||||
* @returns the newly set email
|
||||
*/
|
||||
export async function verifyPasskeyRegistration(
|
||||
passkeyId: string,
|
||||
passkeyName: string,
|
||||
publicKeyCredential:
|
||||
| {
|
||||
[key: string]: any;
|
||||
}
|
||||
| undefined,
|
||||
userId: string,
|
||||
request: PartialMessage<VerifyPasskeyRegistrationRequest>,
|
||||
) {
|
||||
return userService.verifyPasskeyRegistration(
|
||||
{
|
||||
passkeyId,
|
||||
passkeyName,
|
||||
publicKeyCredential,
|
||||
userId,
|
||||
},
|
||||
{},
|
||||
);
|
||||
// TODO: find a better way to handle this
|
||||
request = VerifyPasskeyRegistrationRequest.fromJson(request as any);
|
||||
return userService.verifyPasskeyRegistration(request, {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,11 +18,14 @@ export function middleware(request: NextRequest) {
|
||||
requestHeaders.set("x-zitadel-login-client", SERVICE_USER_ID);
|
||||
|
||||
// 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-public-host", `${request.nextUrl.host}`);
|
||||
// requestHeaders.set("x-zitadel-forwarded", `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
|
||||
// requestHeaders.set("x-zitadel-instance-host", `${INSTANCE}`);
|
||||
requestHeaders.set(
|
||||
"x-zitadel-instance-host",
|
||||
`${INSTANCE}`.replace("https://", ""),
|
||||
);
|
||||
|
||||
const responseHeaders = new Headers();
|
||||
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
||||
|
||||
@@ -8,6 +8,7 @@ import Alert from "./Alert";
|
||||
import { Spinner } from "./Spinner";
|
||||
import BackButton from "./BackButton";
|
||||
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
|
||||
type Props = {
|
||||
@@ -78,7 +79,7 @@ export default function LoginPasskey({
|
||||
loginName,
|
||||
sessionId,
|
||||
organization,
|
||||
challenges: {
|
||||
challenges: RequestChallenges.fromJson({
|
||||
webAuthN: {
|
||||
domain: "",
|
||||
// USER_VERIFICATION_REQUIREMENT_UNSPECIFIED = 0;
|
||||
@@ -87,7 +88,7 @@ export default function LoginPasskey({
|
||||
// USER_VERIFICATION_REQUIREMENT_DISCOURAGED = 3; - mfa
|
||||
userVerificationRequirement: userVerificationRequirement,
|
||||
},
|
||||
},
|
||||
}),
|
||||
authRequestId,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function RegisterPasskey({
|
||||
return submitRegister().then((resp: RegisterPasskeyResponse) => {
|
||||
const passkeyId = resp.passkeyId;
|
||||
const options: CredentialCreationOptions =
|
||||
(resp.publicKeyCredentialCreationOptions?.toJson() as CredentialCreationOptions) ??
|
||||
(resp.publicKeyCredentialCreationOptions as CredentialCreationOptions) ??
|
||||
{};
|
||||
|
||||
if (options?.publicKey) {
|
||||
@@ -143,6 +143,7 @@ export default function RegisterPasskey({
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
return submitVerify(passkeyId, "", data, sessionId).then(() => {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
@@ -194,19 +195,32 @@ export default function RegisterPasskey({
|
||||
type="button"
|
||||
variant={ButtonVariants.Secondary}
|
||||
onClick={() => {
|
||||
const params = new URLSearchParams();
|
||||
if (authRequestId) {
|
||||
params.set("authRequest", authRequestId);
|
||||
}
|
||||
if (sessionId) {
|
||||
params.set("sessionId", sessionId);
|
||||
}
|
||||
const params = new URLSearchParams({
|
||||
authRequest: authRequestId,
|
||||
});
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
if (sessionId) {
|
||||
params.set("sessionId", sessionId);
|
||||
}
|
||||
|
||||
router.push("/login?" + params);
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function RegisterU2F({
|
||||
return submitRegister().then((resp: RegisterU2FResponse) => {
|
||||
const u2fId = resp.u2fId;
|
||||
const options: CredentialCreationOptions =
|
||||
(resp.publicKeyCredentialCreationOptions?.toJson() as CredentialCreationOptions) ??
|
||||
(resp.publicKeyCredentialCreationOptions as CredentialCreationOptions) ??
|
||||
{};
|
||||
|
||||
if (options.publicKey) {
|
||||
|
||||
@@ -121,11 +121,12 @@ export default function SessionItem({
|
||||
|
||||
<XCircleIcon
|
||||
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(() => {
|
||||
reload();
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
import { ReactNode, useState } from "react";
|
||||
|
||||
import { ReactNode, useState } from "react";
|
||||
import {
|
||||
SignInWithGitlab,
|
||||
SignInWithAzureAD,
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
SignInWithGithub,
|
||||
} from "@zitadel/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ProviderSlug } from "@/lib/demos";
|
||||
import Alert from "./Alert";
|
||||
import BackButton from "./BackButton";
|
||||
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 {
|
||||
children?: ReactNode;
|
||||
@@ -36,7 +36,7 @@ export function SignInWithIDP({
|
||||
const [error, setError] = useState<string>("");
|
||||
const router = useRouter();
|
||||
|
||||
async function startFlow(idpId: string, provider: ProviderSlug) {
|
||||
async function startFlow(idpId: string, provider: string) {
|
||||
setLoading(true);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
@@ -78,62 +78,65 @@ export function SignInWithIDP({
|
||||
{identityProviders &&
|
||||
identityProviders.map((idp, i) => {
|
||||
switch (idp.type) {
|
||||
case 6: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITHUB:
|
||||
case IdentityProviderType.GITHUB:
|
||||
return (
|
||||
<SignInWithGithub
|
||||
key={`idp-${i}`}
|
||||
onClick={() =>
|
||||
startFlow(idp.id, ProviderSlug.GITHUB).then(
|
||||
({ authUrl }) => {
|
||||
router.push(authUrl);
|
||||
},
|
||||
)
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITHUB),
|
||||
).then(({ authUrl }) => {
|
||||
router.push(authUrl);
|
||||
})
|
||||
}
|
||||
></SignInWithGithub>
|
||||
);
|
||||
case 7: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITHUB_ES:
|
||||
case IdentityProviderType.GITHUB_ES:
|
||||
return (
|
||||
<SignInWithGithub
|
||||
key={`idp-${i}`}
|
||||
onClick={() => alert("TODO: unimplemented")}
|
||||
></SignInWithGithub>
|
||||
);
|
||||
case 5: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_AZURE_AD:
|
||||
case IdentityProviderType.AZURE_AD:
|
||||
return (
|
||||
<SignInWithAzureAD
|
||||
key={`idp-${i}`}
|
||||
onClick={() =>
|
||||
startFlow(idp.id, ProviderSlug.AZURE).then(
|
||||
({ authUrl }) => {
|
||||
router.push(authUrl);
|
||||
},
|
||||
)
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.AZURE_AD),
|
||||
).then(({ authUrl }) => {
|
||||
router.push(authUrl);
|
||||
})
|
||||
}
|
||||
></SignInWithAzureAD>
|
||||
);
|
||||
case 10: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GOOGLE:
|
||||
case IdentityProviderType.GOOGLE:
|
||||
return (
|
||||
<SignInWithGoogle
|
||||
key={`idp-${i}`}
|
||||
e2e="google"
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(idp.id, ProviderSlug.GOOGLE).then(
|
||||
({ authUrl }) => {
|
||||
router.push(authUrl);
|
||||
},
|
||||
)
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GOOGLE),
|
||||
).then(({ authUrl }) => {
|
||||
router.push(authUrl);
|
||||
})
|
||||
}
|
||||
></SignInWithGoogle>
|
||||
);
|
||||
case 8: // IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITLAB:
|
||||
case IdentityProviderType.GITLAB:
|
||||
return (
|
||||
<SignInWithGitlab
|
||||
key={`idp-${i}`}
|
||||
onClick={() => alert("TODO: unimplemented")}
|
||||
></SignInWithGitlab>
|
||||
);
|
||||
case 9: //IdentityProviderType.IDENTITY_PROVIDER_TYPE_GITLAB_SELF_HOSTED:
|
||||
case IdentityProviderType.GITLAB_SELF_HOSTED:
|
||||
return (
|
||||
<SignInWithGitlab
|
||||
key={`idp-${i}`}
|
||||
|
||||
@@ -42,7 +42,9 @@ export default function UserAvatar({
|
||||
loginName={loginName ?? ""}
|
||||
/>
|
||||
</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>
|
||||
{showDropdown && (
|
||||
<Link
|
||||
|
||||
@@ -38,7 +38,6 @@ export async function createSessionAndUpdateCookie(
|
||||
? {
|
||||
user: { search: { case: "loginName", value: loginName } },
|
||||
password: { password },
|
||||
// totp: { code: totpCode },
|
||||
}
|
||||
: { user: { search: { case: "loginName", value: loginName } } },
|
||||
challenges,
|
||||
@@ -50,7 +49,7 @@ export async function createSessionAndUpdateCookie(
|
||||
createdSession.sessionToken,
|
||||
).then((response) => {
|
||||
if (response?.session && response.session?.factors?.user?.loginName) {
|
||||
const sessionCookie: any = {
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: createdSession.sessionId,
|
||||
token: createdSession.sessionToken,
|
||||
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||
@@ -103,7 +102,7 @@ export async function createSessionForUserIdAndUpdateCookie(
|
||||
createdSession.sessionToken,
|
||||
).then((response) => {
|
||||
if (response?.session && response.session?.factors?.user?.loginName) {
|
||||
const sessionCookie: any = {
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: createdSession.sessionId,
|
||||
token: createdSession.sessionToken,
|
||||
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||
@@ -153,7 +152,7 @@ export async function createSessionForIdpAndUpdateCookie(
|
||||
createdSession.sessionToken,
|
||||
).then((response) => {
|
||||
if (response?.session && response.session?.factors?.user?.loginName) {
|
||||
const sessionCookie: any = {
|
||||
const sessionCookie: CustomCookieData = {
|
||||
id: createdSession.sessionId,
|
||||
token: createdSession.sessionToken,
|
||||
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
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 { OIDCService } from "@zitadel/proto/zitadel/oidc/v2/oidc_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 { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_connect";
|
||||
import { UserService } from "@zitadel/proto/zitadel/user/v2/user_service_connect";
|
||||
|
||||
import { createClientFor } from "./helpers";
|
||||
|
||||
export const createUserServiceClient = createClientFor(UserService);
|
||||
@@ -13,6 +15,7 @@ export const createSessionServiceClient = createClientFor(SessionService);
|
||||
export const createOIDCServiceClient = createClientFor(OIDCService);
|
||||
export const createOrganizationServiceClient = createClientFor(OrganizationService);
|
||||
export const createFeatureServiceClient = createClientFor(FeatureService);
|
||||
export const createIdpServiceClient = createClientFor(IdentityProviderService);
|
||||
|
||||
export function makeReqCtx(orgId: string | undefined): Partial<RequestContext> {
|
||||
return {
|
||||
|
||||
@@ -2,39 +2,20 @@
|
||||
|
||||
import { ReactNode, forwardRef } from "react";
|
||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||
import { IdpButtonClasses } from "./classes";
|
||||
|
||||
export const SignInWithAzureAD = forwardRef<
|
||||
HTMLButtonElement,
|
||||
SignInWithIdentityProviderProps
|
||||
>(
|
||||
export const SignInWithAzureAD = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||
<button
|
||||
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}
|
||||
>
|
||||
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||
<div className="ztdl-h-12 ztdl-p-[10px] ztdl-w-12 ztdl-flex ztdl-items-center ztdl-justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 21 21"
|
||||
className="w-full h-full"
|
||||
>
|
||||
<svg 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="#00a4ef" d="M1 11H10V20H1z"></path>
|
||||
<path fill="#7fba00" d="M11 1H20V10H11z"></path>
|
||||
<path fill="#ffb900" d="M11 11H20V20H11z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<span className="ztdl-ml-4">
|
||||
{name ? name : "Sign in with AzureAD"}
|
||||
</span>
|
||||
)}
|
||||
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with AzureAD"}</span>}
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
@@ -2,18 +2,11 @@
|
||||
|
||||
import { ReactNode, forwardRef } from "react";
|
||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||
import { IdpButtonClasses } from "./classes";
|
||||
|
||||
export const SignInWithGithub = forwardRef<
|
||||
HTMLButtonElement,
|
||||
SignInWithIdentityProviderProps
|
||||
>(
|
||||
export const SignInWithGithub = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||
<button
|
||||
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}
|
||||
>
|
||||
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||
<div className="ztdl-h-8 ztdl-w-8 ztdl-mx-2 flex items-center justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -46,11 +39,7 @@ export const SignInWithGithub = forwardRef<
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<span className="ztdl-ml-4">{name ? name : "Sign in with GitHub"}</span>
|
||||
)}
|
||||
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with GitHub"}</span>}
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
import { ReactNode, forwardRef } from "react";
|
||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||
import { IdpButtonClasses } from "./classes";
|
||||
|
||||
export const SignInWithGitlab = forwardRef<
|
||||
HTMLButtonElement,
|
||||
SignInWithIdentityProviderProps
|
||||
>(
|
||||
export const SignInWithGitlab = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||
<button
|
||||
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}
|
||||
>
|
||||
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||
<div className="ztdl-h-12 ztdl-w-12 flex items-center justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={25}
|
||||
height={24}
|
||||
fill="none"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={25} height={24} fill="none">
|
||||
<path
|
||||
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"
|
||||
@@ -37,11 +25,7 @@ export const SignInWithGitlab = forwardRef<
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<span className="ztdl-ml-4">{name ? name : "Sign in with GitLab"}</span>
|
||||
)}
|
||||
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with GitLab"}</span>}
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
import { ReactNode, forwardRef } from "react";
|
||||
import { SignInWithIdentityProviderProps } from "./SignInWith";
|
||||
import { IdpButtonClasses } from "./classes";
|
||||
|
||||
export const SignInWithGoogle = forwardRef<
|
||||
HTMLButtonElement,
|
||||
SignInWithIdentityProviderProps
|
||||
>(
|
||||
export const SignInWithGoogle = forwardRef<HTMLButtonElement, SignInWithIdentityProviderProps>(
|
||||
({ children, className = "", name = "", ...props }, ref): ReactNode => (
|
||||
<button
|
||||
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}
|
||||
>
|
||||
<button type="button" ref={ref} className={`${IdpButtonClasses} ${className}`} {...props}>
|
||||
<div className="ztdl-h-12 ztdl-w-12 ztdl-flex ztdl-items-center ztdl-justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlSpace="preserve"
|
||||
id="Capa_1"
|
||||
viewBox="0 0 150 150"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlSpace="preserve" id="Capa_1" viewBox="0 0 150 150">
|
||||
<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}"
|
||||
@@ -50,11 +38,7 @@ export const SignInWithGoogle = forwardRef<
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<span className="ztdl-ml-4">{name ? name : "Sign in with Google"}</span>
|
||||
)}
|
||||
{children ? children : <span className="ztdl-ml-4">{name ? name : "Sign in with Google"}</span>}
|
||||
</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