sessioh helper, client side request

This commit is contained in:
Max Peintner
2023-06-29 19:06:30 +02:00
parent e25fa6a228
commit f324407a83
7 changed files with 276 additions and 337 deletions

View File

@@ -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<SessionAuthMethods> {
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<string | number | symbol, string | undefined>;
}) {
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 }));
}
}
const loginSettings = await getLoginSettings(server);
return (
<div className="flex flex-col items-center space-y-4">
<h1>Welcome back!</h1>
<p className="ztdl-p">Enter your login data.</p>
<UsernameForm />
<UsernameForm loginSettings={loginSettings} loginName={loginName} />
</div>
);
} else {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Welcome back!</h1>
<p className="ztdl-p">Enter your login data.</p>
<UsernameForm />
</div>
);
}
}

View File

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

View File

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

View File

@@ -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,64 +47,22 @@ 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 }
);
return setSessionAndUpdateCookie(
recent.id,
recent.token,
recent.loginName,
password,
domain,
challenges
).then((session) => {
return NextResponse.json({
sessionId: session.id,
factors: session.factors,
});
} 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 });
});
})
.catch((error) => {

View File

@@ -114,12 +114,13 @@ export async function setSession(
server: ZitadelServer,
sessionId: string,
sessionToken: string,
domain: string | undefined,
password: string | undefined,
challenges: ChallengeKind[] | undefined
): Promise<SetSessionResponse | undefined> {
const sessionService = session.getSession(server);
const payload = { sessionId, sessionToken, challenges };
const payload = { sessionId, sessionToken, challenges, domain };
return password
? sessionService.setSession(
{

View File

@@ -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<Inputs>({
mode: "onBlur",
defaultValues: {
loginName: loginName ? loginName : "",
},
});
const router = useRouter();
const [loading, setLoading] = useState<boolean>(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");
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 })
// );
// }
// return res.json();
}
});
}
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 && <Spinner className="h-5 w-5 mr-2" />}
continue

105
apps/login/utils/session.ts Normal file
View File

@@ -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<Session> {
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<Session> {
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";
}
});
}