mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 05:12:20 +00:00
signin with idp routing
This commit is contained in:
@@ -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}>
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
88
apps/login/ui/IdpSignin.tsx
Normal file
88
apps/login/ui/IdpSignin.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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: {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user