diff --git a/apps/login/app/(login)/loginname/page.tsx b/apps/login/app/(login)/loginname/page.tsx index 4ff5cd556e7..c62ce175a10 100644 --- a/apps/login/app/(login)/loginname/page.tsx +++ b/apps/login/app/(login)/loginname/page.tsx @@ -1,63 +1,5 @@ -import { - getLoginSettings, - listAuthenticationMethodTypes, - server, -} from "#/lib/zitadel"; +import { getLoginSettings, server } from "#/lib/zitadel"; import UsernameForm from "#/ui/UsernameForm"; -import { AuthenticationMethodType, Factors } from "@zitadel/server"; -import { redirect } from "next/navigation"; - -type SessionAuthMethods = { - authMethodTypes: AuthenticationMethodType[]; - sessionId: string; - factors: Factors; -}; - -async function createSessionAndCookie(loginName: string) { - const res = await fetch( - `${process.env.VERCEL_URL ?? "http://localhost:3000"}/session`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - loginName, - }), - next: { revalidate: 0 }, - } - ); - - const response = await res.json(); - - if (!res.ok) { - return Promise.reject(response.details); - } - return response; -} - -async function createSessionAndGetAuthMethods( - loginName: string -): Promise { - return createSessionAndCookie(loginName) - .then((resp) => { - return listAuthenticationMethodTypes(resp.factors.user.id) - .then((methods) => { - return { - authMethodTypes: methods.authMethodTypes, - sessionId: resp.sessionId, - factors: resp?.factors, - }; - }) - .catch((error) => { - throw "Could not get auth methods"; - }); - }) - .catch((error) => { - console.log(error); - throw "Could not add session to cookie"; - }); -} export default async function Page({ searchParams, @@ -65,40 +7,15 @@ export default async function Page({ searchParams: Record; }) { const loginName = searchParams?.loginName; - if (loginName) { - const login = await getLoginSettings(server); - const sessionAndAuthMethods = await createSessionAndGetAuthMethods( - loginName - ); - if (sessionAndAuthMethods.authMethodTypes.length == 1) { - const method = sessionAndAuthMethods.authMethodTypes[0]; - switch (method) { - case AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSWORD: - return redirect("/password?" + new URLSearchParams({ loginName })); - case AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY: - return redirect( - "/passkey/login?" + new URLSearchParams({ loginName }) - ); - default: - return redirect("/password?" + new URLSearchParams({ loginName })); - } - } - return ( -
-

Welcome back!

-

Enter your login data.

- -
- ); - } else { - return ( -
-

Welcome back!

-

Enter your login data.

+ const loginSettings = await getLoginSettings(server); - -
- ); - } + return ( +
+

Welcome back!

+

Enter your login data.

+ + +
+ ); } diff --git a/apps/login/app/loginnames/route.ts b/apps/login/app/loginnames/route.ts new file mode 100644 index 00000000000..8c77c65bdd4 --- /dev/null +++ b/apps/login/app/loginnames/route.ts @@ -0,0 +1,84 @@ +import { + createSession, + getSession, + listAuthenticationMethodTypes, + server, +} from "#/lib/zitadel"; +import { + SessionCookie, + addSessionToCookie, + getSessionCookieById, +} from "#/utils/cookies"; +import { createSessionAndUpdateCookie } from "#/utils/session"; +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(request: NextRequest) { + const { searchParams } = new URL(request.url); + const sessionId = searchParams.get("sessionId"); + if (sessionId) { + const sessionCookie = await getSessionCookieById(sessionId); + + const session = await getSession( + server, + sessionCookie.id, + sessionCookie.token + ); + + const userId = session?.session?.factors?.user?.id; + + if (userId) { + return listAuthenticationMethodTypes(userId) + .then((methods) => { + return NextResponse.json(methods); + }) + .catch((error) => { + return NextResponse.json(error, { status: 500 }); + }); + } else { + return NextResponse.json( + { details: "could not get session" }, + { status: 500 } + ); + } + } else { + return NextResponse.json({}, { status: 400 }); + } +} + +export async function POST(request: NextRequest) { + const body = await request.json(); + if (body) { + const { loginName } = body; + + const domain: string = request.nextUrl.hostname; + + return createSessionAndUpdateCookie(loginName, undefined, domain, undefined) + .then((session) => { + if (session.factors?.user?.id) { + return listAuthenticationMethodTypes(session.factors?.user?.id) + .then((methods) => { + return NextResponse.json({ + authMethodTypes: methods.authMethodTypes, + sessionId: session.id, + factors: session.factors, + }); + }) + .catch((error) => { + return NextResponse.json(error, { status: 500 }); + }); + } else { + throw "No user id found in session"; + } + }) + .catch((error) => { + return NextResponse.json( + { + details: "could not add session to cookie", + }, + { status: 500 } + ); + }); + } else { + return NextResponse.error(); + } +} diff --git a/apps/login/app/methods/route.ts b/apps/login/app/methods/route.ts deleted file mode 100644 index 70e58d9a210..00000000000 --- a/apps/login/app/methods/route.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { - createSession, - getSession, - listAuthenticationMethodTypes, - server, -} from "#/lib/zitadel"; -import { - SessionCookie, - addSessionToCookie, - getSessionCookieById, -} from "#/utils/cookies"; -import { NextRequest, NextResponse } from "next/server"; - -export async function GET(request: NextRequest) { - const { searchParams } = new URL(request.url); - const sessionId = searchParams.get("sessionId"); - if (sessionId) { - const sessionCookie = await getSessionCookieById(sessionId); - - const session = await getSession( - server, - sessionCookie.id, - sessionCookie.token - ); - - const userId = session?.session?.factors?.user?.id; - - if (userId) { - return listAuthenticationMethodTypes(userId) - .then((methods) => { - return NextResponse.json(methods); - }) - .catch((error) => { - return NextResponse.json(error, { status: 500 }); - }); - } else { - return NextResponse.json( - { details: "could not get session" }, - { status: 500 } - ); - } - } else { - return NextResponse.json({}, { status: 400 }); - } -} - -export async function POST(request: NextRequest) { - const body = await request.json(); - if (body) { - const { loginName } = body; - - const domain: string = request.nextUrl.hostname; - - const createdSession = await createSession( - server, - loginName, - domain, - undefined, - undefined - ); - - if (createdSession) { - return getSession( - server, - createdSession.sessionId, - createdSession.sessionToken - ).then((response) => { - console.log(response); - if (response?.session && response.session?.factors?.user?.loginName) { - const userId = response?.session?.factors?.user?.id; - - const sessionCookie: SessionCookie = { - id: createdSession.sessionId, - token: createdSession.sessionToken, - changeDate: response.session.changeDate?.toString() ?? "", - loginName: response.session?.factors?.user?.loginName ?? "", - }; - return addSessionToCookie(sessionCookie) - .then(() => { - return listAuthenticationMethodTypes(userId) - .then((methods) => { - return NextResponse.json({ - authMethodTypes: methods.authMethodTypes, - sessionId: createdSession.sessionId, - factors: response?.session?.factors, - }); - }) - .catch((error) => { - return NextResponse.json(error, { status: 500 }); - }); - }) - .catch((error) => { - return NextResponse.json( - { - details: "could not add session to cookie", - }, - { status: 500 } - ); - }); - } else { - return NextResponse.json( - { - details: - "could not get session or session does not have loginName", - }, - { status: 500 } - ); - } - }); - } else { - return NextResponse.error(); - } - } else { - return NextResponse.error(); - } -} diff --git a/apps/login/app/session/route.ts b/apps/login/app/session/route.ts index fd47fac0d3e..7a754329c91 100644 --- a/apps/login/app/session/route.ts +++ b/apps/login/app/session/route.ts @@ -1,19 +1,15 @@ -import { - createSession, - getSession, - server, - setSession, - deleteSession, -} from "#/lib/zitadel"; +import { server, deleteSession } from "#/lib/zitadel"; import { SessionCookie, - addSessionToCookie, getMostRecentSessionCookie, getSessionCookieById, getSessionCookieByLoginName, removeSessionFromCookie, - updateSessionCookie, } from "#/utils/cookies"; +import { + createSessionAndUpdateCookie, + setSessionAndUpdateCookie, +} from "#/utils/session"; import { NextRequest, NextResponse } from "next/server"; export async function POST(request: NextRequest) { @@ -23,47 +19,7 @@ export async function POST(request: NextRequest) { const domain: string = request.nextUrl.hostname; - const createdSession = await createSession( - server, - loginName, - domain, - password, - undefined - ); - - if (createdSession) { - return getSession( - server, - createdSession.sessionId, - createdSession.sessionToken - ).then((response) => { - if (response?.session && response.session?.factors?.user?.loginName) { - const sessionCookie: SessionCookie = { - id: createdSession.sessionId, - token: createdSession.sessionToken, - changeDate: response.session.changeDate?.toString() ?? "", - loginName: response.session?.factors?.user?.loginName ?? "", - }; - - return addSessionToCookie(sessionCookie).then(() => { - return NextResponse.json({ - sessionId: createdSession.sessionId, - factors: response?.session?.factors, - }); - }); - } else { - return NextResponse.json( - { - details: - "could not get session or session does not have loginName", - }, - { status: 500 } - ); - } - }); - } else { - return NextResponse.error(); - } + return createSessionAndUpdateCookie(loginName, password, domain, undefined); } else { return NextResponse.json( { details: "Session could not be created" }, @@ -91,65 +47,23 @@ export async function PUT(request: NextRequest) { return Promise.reject(error); }); + const domain: string = request.nextUrl.hostname; + return recentPromise .then((recent) => { - return setSession(server, recent.id, recent.token, password, challenges) - .then((session) => { - if (session) { - const sessionCookie: SessionCookie = { - id: recent.id, - token: session.sessionToken, - changeDate: session.details?.changeDate?.toString() ?? "", - loginName: recent.loginName, - }; - - return getSession( - server, - sessionCookie.id, - sessionCookie.token - ).then((response) => { - if ( - response?.session && - response.session.factors?.user?.loginName - ) { - const { session } = response; - const newCookie: SessionCookie = { - id: sessionCookie.id, - token: sessionCookie.token, - changeDate: session.changeDate?.toString() ?? "", - loginName: session.factors?.user?.loginName ?? "", - }; - - return updateSessionCookie(sessionCookie.id, newCookie) - .then(() => { - return NextResponse.json({ factors: session.factors }); - }) - .catch((error) => { - return NextResponse.json( - { details: "could not set cookie" }, - { status: 500 } - ); - }); - } else { - return NextResponse.json( - { - details: - "could not get session or session does not have loginName", - }, - { status: 500 } - ); - } - }); - } else { - return NextResponse.json( - { details: "Session not be set" }, - { status: 500 } - ); - } - }) - .catch((error) => { - return NextResponse.json({ details: error }, { status: 500 }); + return setSessionAndUpdateCookie( + recent.id, + recent.token, + recent.loginName, + password, + domain, + challenges + ).then((session) => { + return NextResponse.json({ + sessionId: session.id, + factors: session.factors, }); + }); }) .catch((error) => { return NextResponse.json({ details: error }, { status: 500 }); diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index b6f3651acd9..6e1ffae0552 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -114,12 +114,13 @@ export async function setSession( server: ZitadelServer, sessionId: string, sessionToken: string, + domain: string | undefined, password: string | undefined, challenges: ChallengeKind[] | undefined ): Promise { const sessionService = session.getSession(server); - const payload = { sessionId, sessionToken, challenges }; + const payload = { sessionId, sessionToken, challenges, domain }; return password ? sessionService.setSession( { diff --git a/apps/login/ui/UsernameForm.tsx b/apps/login/ui/UsernameForm.tsx index 7e9503ace07..7b621573dd5 100644 --- a/apps/login/ui/UsernameForm.tsx +++ b/apps/login/ui/UsernameForm.tsx @@ -6,40 +6,74 @@ import { TextInput } from "./Input"; import { useForm } from "react-hook-form"; import { useRouter } from "next/navigation"; import { Spinner } from "./Spinner"; +import { AuthenticationMethodType, LoginSettings } from "@zitadel/server"; type Inputs = { loginName: string; }; -export default function UsernameForm() { +type Props = { + loginSettings: LoginSettings | undefined; + loginName: string | undefined; +}; + +export default function UsernameForm({ loginSettings, loginName }: Props) { const { register, handleSubmit, formState } = useForm({ mode: "onBlur", + defaultValues: { + loginName: loginName ? loginName : "", + }, }); const router = useRouter(); const [loading, setLoading] = useState(false); - function resubmitWithUsername(values: Inputs) { - return router.push( - `/loginname?` + new URLSearchParams({ loginName: values.loginName }) - ); - // setLoading(true); - // const res = await fetch("/methods", { - // method: "POST", - // headers: { - // "Content-Type": "application/json", - // }, - // body: JSON.stringify({ - // loginName: values.loginName, - // }), - // }); + async function submitLoginName(values: Inputs) { + setLoading(true); + const res = await fetch("/loginnames", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + loginName: values.loginName, + }), + }); - // setLoading(false); - // if (!res.ok) { - // throw new Error("Failed to load authentication methods"); - // } - // return res.json(); + setLoading(false); + if (!res.ok) { + throw new Error("Failed to load authentication methods"); + } + return res.json(); + } + + async function setLoginNameAndGetAuthMethods(values: Inputs) { + return submitLoginName(values).then((response) => { + console.log(response); + if (response.authMethodTypes.length == 1) { + const method = response.authMethodTypes[0]; + console.log(method); + // switch (method) { + // case AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSWORD: + // return router.push( + // "/password?" + + // new URLSearchParams({ loginName: values.loginName }) + // ); + // case AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY: + // break; + // // return router.push( + // // "/passkey/login?" + + // // new URLSearchParams({ loginName: values.loginName }) + // // ); + // default: + // return router.push( + // "/password?" + + // new URLSearchParams({ loginName: values.loginName }) + // ); + // } + } + }); } const { errors } = formState; @@ -66,7 +100,7 @@ export default function UsernameForm() { className="self-end" variant={ButtonVariants.Primary} disabled={loading || !formState.isValid} - onClick={handleSubmit(resubmitWithUsername)} + onClick={handleSubmit(setLoginNameAndGetAuthMethods)} > {loading && } continue diff --git a/apps/login/utils/session.ts b/apps/login/utils/session.ts new file mode 100644 index 00000000000..79442b800c2 --- /dev/null +++ b/apps/login/utils/session.ts @@ -0,0 +1,105 @@ +import { createSession, getSession, server, setSession } from "#/lib/zitadel"; +import { NextResponse } from "next/server"; +import { + SessionCookie, + addSessionToCookie, + updateSessionCookie, +} from "./cookies"; +import { ChallengeKind, Session } from "@zitadel/server"; + +export async function createSessionAndUpdateCookie( + loginName: string, + password: string | undefined, + domain: string, + challenges: ChallengeKind[] | undefined +): Promise { + const createdSession = await createSession( + server, + loginName, + domain, + password, + challenges + ); + + if (createdSession) { + return getSession( + server, + createdSession.sessionId, + createdSession.sessionToken + ).then((response) => { + if (response?.session && response.session?.factors?.user?.loginName) { + const sessionCookie: SessionCookie = { + id: createdSession.sessionId, + token: createdSession.sessionToken, + changeDate: response.session.changeDate?.toString() ?? "", + loginName: response.session?.factors?.user?.loginName ?? "", + }; + + return addSessionToCookie(sessionCookie).then(() => { + return response.session as Session; + // { + // sessionId: createdSession.sessionId, + // factors: response?.session?.factors, + // }); + }); + } else { + throw "could not get session or session does not have loginName"; + } + }); + } else { + throw "Could not create session"; + } +} + +export async function setSessionAndUpdateCookie( + sessionId: string, + sessionToken: string, + loginName: string, + password: string | undefined, + domain: string | undefined, + challenges: ChallengeKind[] | undefined +): Promise { + return setSession( + server, + sessionId, + sessionToken, + domain, + password, + challenges + ).then((session) => { + if (session) { + const sessionCookie: SessionCookie = { + id: sessionId, + token: session.sessionToken, + changeDate: session.details?.changeDate?.toString() ?? "", + loginName: loginName, + }; + + return getSession(server, sessionCookie.id, sessionCookie.token).then( + (response) => { + if (response?.session && response.session.factors?.user?.loginName) { + const { session } = response; + const newCookie: SessionCookie = { + id: sessionCookie.id, + token: sessionCookie.token, + changeDate: session.changeDate?.toString() ?? "", + loginName: session.factors?.user?.loginName ?? "", + }; + + return updateSessionCookie(sessionCookie.id, newCookie) + .then(() => { + return session; + }) + .catch((error) => { + throw "could not set cookie"; + }); + } else { + throw "could not get session or session does not have loginName"; + } + } + ); + } else { + throw "Session not be set"; + } + }); +}