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 d203f750fd3..8ee5551c70b 100644 --- a/apps/login/src/app/(login)/idp/[provider]/success/page.tsx +++ b/apps/login/src/app/(login)/idp/[provider]/success/page.tsx @@ -3,6 +3,7 @@ import { addIDPLink, createUser, getBrandingSettings, + getIDPByID, retrieveIDPIntent, } from "@/lib/zitadel"; import Alert, { AlertType } from "@/ui/Alert"; @@ -77,6 +78,7 @@ const PROVIDER_MAPPING: { }, idpLinks: [idpLink], }; + return req; }, [ProviderSlug.GITHUB]: (idp: IDPInformation) => { @@ -120,76 +122,132 @@ export default async function Page({ const branding = await getBrandingSettings(organization); if (provider && id && token) { return retrieveIDPIntent(id, token) - .then((resp) => { + .then(async (resp) => { const { idpInformation, userId } = resp; - if (idpInformation) { - if (userId) { - return ( - -
-

Login successful

-
You have successfully been loggedIn!
+ if (userId) { + // TODO: update user if idp.options.isAutoUpdate is true - -
-
- ); - } else { - return createUser(provider, idpInformation) - .then((userId) => { + return ( + +
+

Login successful

+
You have successfully been loggedIn!
+ + +
+
+ ); + } + + if (idpInformation) { + const idp = await getIDPByID(idpInformation.idpId); + const options = idp?.config?.options; + + // search for potential user via username, then link + if (options?.isLinkingAllowed) { + const userId = ""; + + const idpLink = await addIDPLink( + { + id: idpInformation.idpId, + userId: idpInformation.userId, + userName: idpInformation.userName, + }, + userId, + ).catch((error) => { + return ( + +
+

Linking failed

+
+ { + + {JSON.stringify(error.message)} + + } +
+
+
+ ); + }); + + if (idpLink) { + return ( + +
+

Account successfully linked

+
Your account has successfully been linked!
+
+
+ ); + } + } else if (options?.isCreationAllowed && options.isAutoCreation) { + const userId = await createUser(provider, idpInformation).catch( + (error) => { return (
-

Register successful

-
You have successfully been registered!
+

Register failed

+
+ { + + {JSON.stringify(error.message)} + + } +
); - }) - .catch((error) => { - 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)} - - } -
-
-
- ); - } - }); + }, + ); + + if (userId) { + return ( + +
+

Register successful

+
You have successfully been registered!
+
+
+ ); + } } + + // return login failed if no linking or creation is allowed and no user was found + return ( + +
+

Login failed

+
+ { + + User could not be logged in + + } +
+
+
+ ); } else { - throw new Error("Could not get user information."); + return ( + +
+

Login failed

+
+ { + + Could not get user information + + } +
+
+
+ ); } }) .catch((error) => { diff --git a/apps/login/src/lib/zitadel.ts b/apps/login/src/lib/zitadel.ts index 1e2f70e52ad..7bafd46df5f 100644 --- a/apps/login/src/lib/zitadel.ts +++ b/apps/login/src/lib/zitadel.ts @@ -3,6 +3,7 @@ import { createSessionServiceClient, createSettingsServiceClient, createUserServiceClient, + createIdpServiceClient, makeReqCtx, } from "@zitadel/client/v2"; import { createManagementServiceClient } from "@zitadel/client/v1"; @@ -12,13 +13,15 @@ import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_p import { RetrieveIdentityProviderIntentRequest, VerifyU2FRegistrationRequest, + AddHumanUserRequest, } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; +import { IDPInformation, IDPLink } 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"; const SESSION_LIFETIME_S = 3000; @@ -34,6 +37,7 @@ 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); @@ -355,6 +359,10 @@ export function retrieveIDPIntent(id: string, token: string) { ); } +export function getIDPByID(id: string) { + return idpService.getIDPByID({ id }, {}).then((resp) => resp.idp); +} + export function addIDPLink( idp: { id: string; diff --git a/packages/zitadel-client/src/v2.ts b/packages/zitadel-client/src/v2.ts index 21461498941..f840f3ac048 100644 --- a/packages/zitadel-client/src/v2.ts +++ b/packages/zitadel-client/src/v2.ts @@ -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 { return {