This commit is contained in:
peintnermax
2024-08-21 14:15:44 +02:00
parent 2379f87a20
commit ab0ac166c9
8 changed files with 167 additions and 194 deletions

View File

@@ -1,4 +1,3 @@
import { ProviderSlug } from "@/lib/demos";
import { getBrandingSettings, PROVIDER_NAME_MAPPING } from "@/lib/zitadel"; import { getBrandingSettings, PROVIDER_NAME_MAPPING } from "@/lib/zitadel";
import DynamicTheme from "@/ui/DynamicTheme"; import DynamicTheme from "@/ui/DynamicTheme";
@@ -7,7 +6,7 @@ export default async function Page({
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;

View File

@@ -1,4 +1,3 @@
import { ProviderSlug } from "@/lib/demos";
import { import {
addIDPLink, addIDPLink,
createUser, createUser,
@@ -11,18 +10,14 @@ import {
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 { IDPInformation, IDPLink } from "@zitadel/proto/zitadel/user/v2/idp_pb";
import { AutoLinkingOption } from "@zitadel/proto/zitadel/idp/v2/idp_pb"; import { AutoLinkingOption } from "@zitadel/proto/zitadel/idp/v2/idp_pb";
import { PartialMessage } from "@zitadel/client";
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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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",

125
apps/login/src/lib/idp.ts Normal file
View File

@@ -0,0 +1,125 @@
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";
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");
}
}
export const PROVIDER_NAME_MAPPING: {
[provider: string]: string;
} = {
[IdentityProviderType.GOOGLE]: "Google",
[IdentityProviderType.GITHUB]: "GitHub",
[IdentityProviderType.AZURE_AD]: "Microsoft",
};
export const PROVIDER_MAPPING: {
[provider: string]: (
rI: IDPInformation,
) => PartialMessage<AddHumanUserRequest>;
} = {
[IdentityProviderType.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;
},
[IdentityProviderType.AZURE_AD]: (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;
},
[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 },
},
// organisation: Organisation | undefined;
profile: {
displayName: rawInfo?.name ?? "",
givenName: rawInfo?.name ?? "",
familyName: rawInfo?.name ?? "",
},
idpLinks: [idpLink],
};
return req;
},
};

View File

@@ -13,17 +13,16 @@ import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_p
import { import {
RetrieveIdentityProviderIntentRequest, RetrieveIdentityProviderIntentRequest,
VerifyU2FRegistrationRequest, VerifyU2FRegistrationRequest,
AddHumanUserRequest,
} from "@zitadel/proto/zitadel/user/v2/user_service_pb"; } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { IDPInformation, IDPLink } from "@zitadel/proto/zitadel/user/v2/idp_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 { PartialMessage, PlainMessage } from "@zitadel/client";
import VerifyEmailForm from "@/ui/VerifyEmailForm";
import { SearchQuery as UserSearchQuery } from "@zitadel/proto/zitadel/user/v2/query_pb"; 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;
@@ -296,14 +295,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,
@@ -400,104 +391,8 @@ export function addIDPLink(
); );
} }
export 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;
},
};
export function createUser( export function createUser(
provider: ProviderSlug, provider: string,
info: IDPInformation, info: IDPInformation,
): Promise<string> { ): Promise<string> {
const userData = PROVIDER_MAPPING[provider](info); const userData = PROVIDER_MAPPING[provider](info);

View File

@@ -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,
router.push(authUrl); idpTypeToSlug(IdentityProviderType.GITHUB),
}, ).then(({ 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,
router.push(authUrl); idpTypeToSlug(IdentityProviderType.AZURE_AD),
}, ).then(({ 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,
router.push(authUrl); idpTypeToSlug(IdentityProviderType.GOOGLE),
}, ).then(({ 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}`}