signin with idp routing

This commit is contained in:
peintnermax
2024-03-19 14:15:54 +01:00
parent b5bef43888
commit 2b1e5312f4
6 changed files with 156 additions and 54 deletions

View File

@@ -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<AddHumanUserRequest>;
@@ -59,19 +62,15 @@ const PROVIDER_MAPPING: {
},
};
function retrieveIDP(
function retrieveIDPIntent(
id: string,
token: string
): Promise<IDPInformation | undefined> {
): Promise<RetrieveIdentityProviderIntentResponse> {
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 (
<div className="flex flex-col items-center space-y-4">
<h1>Login successful</h1>
<div>You have successfully been loggedIn!</div>
</div>
);
})
.catch((error) => {
throw new Error(error.details);
});
if (userId) {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Login successful</h1>
<div>You have successfully been loggedIn!</div>
<IdpSignin
userId={userId}
idpIntent={{ idpIntentId: id, idpIntentToken: token }}
/>
</div>
);
} else {
// handle register
return createUser(provider, information)
return createUser(provider, idpInformation)
.then((userId) => {
return (
<div className="flex flex-col items-center space-y-4">
@@ -132,19 +121,29 @@ export default async function Page({
</div>
);
})
.catch((error) => {
throw new Error(error.details);
.catch((error: ClientError) => {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Register failed</h1>
<div className="w-full">
{
<Alert type={AlertType.ALERT}>
{JSON.stringify(error.message)}
</Alert>
}
</div>
</div>
);
});
}
} else {
throw new Error("Could not get user information.");
}
})
.catch((error: Error) => {
.catch((error) => {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Register failed</h1>
<h1>An error occurred</h1>
<div className="w-full">
{
<Alert type={AlertType.ALERT}>

View File

@@ -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" },

View File

@@ -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<CreateSessionResponse | undefined> {
const sessionService = session.getSession(server);
return sessionService.createSession(
{
checks: { user: { userId }, idpIntent },

View File

@@ -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<string | null>(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 (
<div className="flex items-center justify-center">
{loading && <Spinner />}
{error && (
<div className="py-4">
<Alert>{error}</Alert>
</div>
)}
</div>
);
}

View File

@@ -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: {

View File

@@ -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<Session> {
const createdSession = await createSession(
const createdSession = await createSessionForLoginname(
server,
loginName,
password,