From 2b1e5312f4528bc5aa4a1a0e9a4ebb61c2e09c33 Mon Sep 17 00:00:00 2001 From: peintnermax Date: Tue, 19 Mar 2024 14:15:54 +0100 Subject: [PATCH] signin with idp routing --- .../(login)/idp/[provider]/success/page.tsx | 83 +++++++++-------- apps/login/app/api/session/route.ts | 29 ++++-- apps/login/lib/zitadel.ts | 3 +- apps/login/ui/IdpSignin.tsx | 88 +++++++++++++++++++ apps/login/ui/PasswordForm.tsx | 1 + apps/login/utils/session.ts | 6 +- 6 files changed, 156 insertions(+), 54 deletions(-) create mode 100644 apps/login/ui/IdpSignin.tsx diff --git a/apps/login/app/(login)/idp/[provider]/success/page.tsx b/apps/login/app/(login)/idp/[provider]/success/page.tsx index 3779054fbec..2101a1f1e3b 100644 --- a/apps/login/app/(login)/idp/[provider]/success/page.tsx +++ b/apps/login/app/(login)/idp/[provider]/success/page.tsx @@ -1,6 +1,7 @@ import { ProviderSlug } from "#/lib/demos"; import { server } from "#/lib/zitadel"; import Alert, { AlertType } from "#/ui/Alert"; +import IdpSignin from "#/ui/IdpSignin"; import { createSessionForIdpAndUpdateCookie } from "#/utils/session"; import { AddHumanUserRequest, @@ -8,7 +9,9 @@ import { RetrieveIdentityProviderIntentResponse, user, IDPLink, + Session, } from "@zitadel/server"; +import { ClientError } from "nice-grpc"; const PROVIDER_MAPPING: { [provider: string]: (rI: IDPInformation) => Partial; @@ -59,19 +62,15 @@ const PROVIDER_MAPPING: { }, }; -function retrieveIDP( +function retrieveIDPIntent( id: string, token: string -): Promise { +): Promise { const userService = user.getUser(server); - return userService - .retrieveIdentityProviderIntent( - { idpIntentId: id, idpIntentToken: token }, - {} - ) - .then((resp: RetrieveIdentityProviderIntentResponse) => { - return resp.idpInformation; - }); + return userService.retrieveIdentityProviderIntent( + { idpIntentId: id, idpIntentToken: token }, + {} + ); } function createUser( @@ -94,36 +93,26 @@ export default async function Page({ const { provider } = params; if (provider && id && token) { - return retrieveIDP(id, token) - .then((information) => { - if (information) { - console.log(information); - + return retrieveIDPIntent(id, token) + .then((resp) => { + const { idpInformation, userId } = resp; + if (idpInformation) { // handle login - if (information.userId) { - return createSessionForIdpAndUpdateCookie( - information.userId, - { - idpIntentId: id, - idpIntentToken: token, - }, - undefined - ) - .then((session) => { - return ( -
-

Login successful

-
You have successfully been loggedIn!
-
- ); - }) - .catch((error) => { - throw new Error(error.details); - }); + if (userId) { + return ( +
+

Login successful

+
You have successfully been loggedIn!
+ + +
+ ); } else { // handle register - - return createUser(provider, information) + return createUser(provider, idpInformation) .then((userId) => { return (
@@ -132,19 +121,29 @@ export default async function Page({
); }) - .catch((error) => { - throw new Error(error.details); + .catch((error: ClientError) => { + return ( +
+

Register failed

+
+ { + + {JSON.stringify(error.message)} + + } +
+
+ ); }); } } else { throw new Error("Could not get user information."); } }) - - .catch((error: Error) => { + .catch((error) => { return (
-

Register failed

+

An error occurred

{ diff --git a/apps/login/app/api/session/route.ts b/apps/login/app/api/session/route.ts index 6c227ec18c1..b9108801909 100644 --- a/apps/login/app/api/session/route.ts +++ b/apps/login/app/api/session/route.ts @@ -8,6 +8,7 @@ import { } from "#/utils/cookies"; import { createSessionAndUpdateCookie, + createSessionForIdpAndUpdateCookie, setSessionAndUpdateCookie, } from "#/utils/session"; import { RequestChallenges } from "@zitadel/server"; @@ -16,16 +17,26 @@ import { NextRequest, NextResponse } from "next/server"; export async function POST(request: NextRequest) { const body = await request.json(); if (body) { - const { loginName, password } = body; + const { userId, idpIntent, loginName, password, authRequestId } = body; - return createSessionAndUpdateCookie( - loginName, - password, - undefined, - undefined - ).then((session) => { - return NextResponse.json(session); - }); + if (userId && idpIntent) { + return createSessionForIdpAndUpdateCookie( + userId, + idpIntent, + authRequestId + ).then((session) => { + return NextResponse.json(session); + }); + } else { + return createSessionAndUpdateCookie( + loginName, + password, + undefined, + undefined + ).then((session) => { + return NextResponse.json(session); + }); + } } else { return NextResponse.json( { details: "Session could not be created" }, diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 28d15ac6d7a..1cbc1731456 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -103,7 +103,7 @@ export async function getPasswordComplexitySettings( .then((resp: GetPasswordComplexitySettingsResponse) => resp.settings); } -export async function createSession( +export async function createSessionForLoginname( server: ZitadelServer, loginName: string, password: string | undefined, @@ -144,6 +144,7 @@ export async function createSessionForUserIdAndIdpIntent( } ): Promise { const sessionService = session.getSession(server); + return sessionService.createSession( { checks: { user: { userId }, idpIntent }, diff --git a/apps/login/ui/IdpSignin.tsx b/apps/login/ui/IdpSignin.tsx new file mode 100644 index 00000000000..ff3a4cf6033 --- /dev/null +++ b/apps/login/ui/IdpSignin.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Spinner } from "./Spinner"; +import Alert from "./Alert"; +import { useRouter } from "next/navigation"; + +type Props = { + userId: string; + idpIntent: { + idpIntentId: string; + idpIntentToken: string; + }; + authRequestId?: string; +}; + +export default function IdpSignin(props: Props) { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const router = useRouter(); + + async function createSessionForIdp() { + setLoading(true); + const res = await fetch("/api/session", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + userId: props.userId, + idpIntent: props.idpIntent, + authRequestId: props.authRequestId, + }), + }); + + if (!res.ok) { + const error = await res.json(); + throw error.details.details; + } + return res.json(); + } + + useEffect(() => { + createSessionForIdp() + .then((session) => { + setLoading(false); + if (props.authRequestId && session && session.sessionId) { + return router.push( + `/login?` + + new URLSearchParams({ + sessionId: session.sessionId, + authRequest: props.authRequestId, + }) + ); + } else { + return router.push( + `/signedin?` + + new URLSearchParams( + props.authRequestId + ? { + loginName: session.factors.user.loginName, + authRequestId: props.authRequestId, + } + : { + loginName: session.factors.user.loginName, + } + ) + ); + } + }) + .catch((error) => { + setLoading(false); + setError(error.message); + }); + }, []); + + return ( +
+ {loading && } + {error && ( +
+ {error} +
+ )} +
+ ); +} diff --git a/apps/login/ui/PasswordForm.tsx b/apps/login/ui/PasswordForm.tsx index e5fd02534ac..49684fa6180 100644 --- a/apps/login/ui/PasswordForm.tsx +++ b/apps/login/ui/PasswordForm.tsx @@ -38,6 +38,7 @@ export default function PasswordForm({ async function submitPassword(values: Inputs) { setError(""); setLoading(true); + const res = await fetch("/api/session", { method: "PUT", headers: { diff --git a/apps/login/utils/session.ts b/apps/login/utils/session.ts index f351f1f4ada..575cbdb823b 100644 --- a/apps/login/utils/session.ts +++ b/apps/login/utils/session.ts @@ -1,5 +1,7 @@ +"use server"; + import { - createSession, + createSessionForLoginname, createSessionForUserIdAndIdpIntent, getSession, server, @@ -18,7 +20,7 @@ export async function createSessionAndUpdateCookie( challenges: RequestChallenges | undefined, authRequestId: string | undefined ): Promise { - const createdSession = await createSession( + const createdSession = await createSessionForLoginname( server, loginName, password,