From 505844d4c54da6bcc62851080f212b6cf0703cd9 Mon Sep 17 00:00:00 2001 From: peintnermax Date: Wed, 17 Jul 2024 17:22:12 +0200 Subject: [PATCH] fix: implement auto linking on register --- .../(login)/idp/[provider]/success/page.tsx | 141 +++++------------- apps/login/src/lib/zitadel.ts | 108 ++++++++++++++ 2 files changed, 148 insertions(+), 101 deletions(-) diff --git a/apps/login/src/app/(login)/idp/[provider]/success/page.tsx b/apps/login/src/app/(login)/idp/[provider]/success/page.tsx index ddcd987e1ca..714c3dc6909 100644 --- a/apps/login/src/app/(login)/idp/[provider]/success/page.tsx +++ b/apps/login/src/app/(login)/idp/[provider]/success/page.tsx @@ -1,94 +1,13 @@ import { ProviderSlug } from "@/lib/demos"; -import { getBrandingSettings, userService } from "@/lib/zitadel"; +import { + addIDPLink, + createUser, + getBrandingSettings, + 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/v2beta/user_service_pb"; -import { - IDPInformation, - IDPLink, -} from "@zitadel/proto/zitadel/user/v2beta/idp_pb"; -import { PartialMessage } from "@zitadel/client2"; - -const PROVIDER_MAPPING: { - [provider: string]: ( - rI: IDPInformation, - ) => PartialMessage; -} = { - [ProviderSlug.GOOGLE]: (idp: IDPInformation) => { - const rawInfo = idp.rawInformation?.toJson() as { - User: { - email: string; - name?: string; - given_name?: string; - family_name?: string; - }; - }; - - const idpLink: PartialMessage = { - idpId: idp.idpId, - userId: idp.userId, - userName: idp.userName, - }; - - const req: PartialMessage = { - 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.GITHUB]: (idp: IDPInformation) => { - const rawInfo = idp.rawInformation?.toJson() as { - email: string; - name: string; - }; - const idpLink: PartialMessage = { - idpId: idp.idpId, - userId: idp.userId, - userName: idp.userName, - }; - const req: PartialMessage = { - 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 { - const userData = PROVIDER_MAPPING[provider](info); - return userService.addHumanUser(userData, {}).then((resp) => resp.userId); -} export default async function Page({ searchParams, @@ -101,11 +20,11 @@ export default async function Page({ const { provider } = params; const branding = await getBrandingSettings(organization); - if (provider && id && token) { return retrieveIDPIntent(id, token) .then((resp) => { const { idpInformation, userId } = resp; + console.log("provider", provider, idpInformation, "userId", userId); if (idpInformation) { // handle login @@ -138,20 +57,40 @@ export default async function Page({ ); }) .catch((error) => { - return ( - -
-

Register failed

-
- { - - {JSON.stringify(error.message)} - - } + if (error.code === 6) { + return addIDPLink( + { + id: idpInformation.idpId, + userId: idpInformation.userId, + userName: idpInformation.userName, + }, + userId, + ).then(() => { + return ( + +
+

Account successfully linked

+
Your account has successfully been linked!
+
+
+ ); + }); + } else { + return ( + +
+

Register failed

+
+ { + + {JSON.stringify(error.message)} + + } +
-
- - ); + + ); + } }); } } else { diff --git a/apps/login/src/lib/zitadel.ts b/apps/login/src/lib/zitadel.ts index 423edadd3d6..c7222630a6a 100644 --- a/apps/login/src/lib/zitadel.ts +++ b/apps/login/src/lib/zitadel.ts @@ -17,6 +17,13 @@ import { CreateCallbackRequest } from "@zitadel/proto/zitadel/oidc/v2beta/oidc_s import { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2beta/object_pb"; import type { RedirectURLs } from "@zitadel/proto/zitadel/user/v2beta/idp_pb"; import { PlainMessage } from "@zitadel/client2"; +import { ProviderSlug } from "./demos"; +import { AddHumanUserRequest } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb"; +import { + IDPInformation, + IDPLink, +} from "@zitadel/proto/zitadel/user/v2beta/idp_pb"; +import { PartialMessage } from "@zitadel/client2"; const SESSION_LIFETIME_S = 3000; @@ -354,6 +361,107 @@ export async function resendEmailCode(userId: string) { ); } +export function retrieveIDPIntent(id: string, token: string) { + return userService.retrieveIdentityProviderIntent( + { idpIntentId: id, idpIntentToken: token }, + {}, + ); +} + +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, + }, + {}, + ); +} + +const PROVIDER_MAPPING: { + [provider: string]: ( + rI: IDPInformation, + ) => PartialMessage; +} = { + [ProviderSlug.GOOGLE]: (idp: IDPInformation) => { + const rawInfo = idp.rawInformation?.toJson() as { + User: { + email: string; + name?: string; + given_name?: string; + family_name?: string; + }; + }; + + const idpLink: PartialMessage = { + idpId: idp.idpId, + userId: idp.userId, + userName: idp.userName, + }; + + const req: PartialMessage = { + 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.GITHUB]: (idp: IDPInformation) => { + const rawInfo = idp.rawInformation?.toJson() as { + email: string; + name: string; + }; + const idpLink: PartialMessage = { + idpId: idp.idpId, + userId: idp.userId, + userName: idp.userName, + }; + const req: PartialMessage = { + 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( + provider: ProviderSlug, + info: IDPInformation, +): Promise { + const userData = PROVIDER_MAPPING[provider](info); + return userService.addHumanUser(userData, {}).then((resp) => resp.userId); +} + /** * * @param userId the id of the user where the email should be set