move flow completion to signedin page

This commit is contained in:
Max Peintner
2025-05-06 15:24:25 +02:00
parent 2f7c628dcd
commit 4588c48fda
13 changed files with 108 additions and 39 deletions

View File

@@ -166,7 +166,11 @@
"signedin": {
"title": "Willkommen {user}!",
"description": "Sie sind angemeldet.",
"continue": "Weiter"
"continue": "Weiter",
"error": {
"title": "Fehler",
"description": "Ein Fehler ist aufgetreten."
}
},
"verify": {
"userIdMissing": "Keine Benutzer-ID angegeben!",

View File

@@ -166,7 +166,11 @@
"signedin": {
"title": "Welcome {user}!",
"description": "You are signed in.",
"continue": "Continue"
"continue": "Continue",
"error": {
"title": "Error",
"description": "An error occurred while trying to sign in."
}
},
"verify": {
"userIdMissing": "No userId provided!",

View File

@@ -166,7 +166,11 @@
"signedin": {
"title": "¡Bienvenido {user}!",
"description": "Has iniciado sesión.",
"continue": "Continuar"
"continue": "Continuar",
"error": {
"title": "Error",
"description": "Ocurrió un error al iniciar sesión."
}
},
"verify": {
"userIdMissing": "¡No se proporcionó userId!",

View File

@@ -166,7 +166,11 @@
"signedin": {
"title": "Benvenuto {user}!",
"description": "Sei connesso.",
"continue": "Continua"
"continue": "Continua",
"error": {
"title": "Errore",
"description": "Si è verificato un errore durante il tentativo di accesso."
}
},
"verify": {
"userIdMissing": "Nessun userId fornito!",

View File

@@ -166,7 +166,11 @@
"signedin": {
"title": "Witaj {user}!",
"description": "Jesteś zalogowany.",
"continue": "Kontynuuj"
"continue": "Kontynuuj",
"error": {
"title": "Błąd",
"description": "Nie można załadować danych. Sprawdź połączenie z internetem lub spróbuj ponownie później."
}
},
"verify": {
"userIdMissing": "Nie podano identyfikatora użytkownika!",

View File

@@ -166,7 +166,11 @@
"signedin": {
"title": "Добро пожаловать, {user}!",
"description": "Вы вошли в систему.",
"continue": "Продолжить"
"continue": "Продолжить",
"error": {
"title": "Ошибка",
"description": "Не удалось войти в систему. Проверьте свои данные и попробуйте снова."
}
},
"verify": {
"userIdMissing": "Не указан userId!",

View File

@@ -166,7 +166,11 @@
"signedin": {
"title": "欢迎 {user}",
"description": "您已登录。",
"continue": "继续"
"continue": "继续",
"error": {
"title": "错误",
"description": "登录时发生错误。"
}
},
"verify": {
"userIdMissing": "未提供用户 ID",

View File

@@ -2,7 +2,11 @@ import { Alert, AlertType } from "@/components/alert";
import { Button, ButtonVariants } from "@/components/button";
import { DynamicTheme } from "@/components/dynamic-theme";
import { UserAvatar } from "@/components/user-avatar";
import { getSessionCookieById } from "@/lib/cookies";
import {
getMostRecentCookieWithLoginname,
getSessionCookieById,
} from "@/lib/cookies";
import { completeDeviceAuthorization } from "@/lib/server/device";
import { getServiceUrlFromHeaders } from "@/lib/service";
import { loadMostRecentSession } from "@/lib/session";
import {
@@ -41,6 +45,36 @@ export default async function Page(props: { searchParams: Promise<any> }) {
const { loginName, requestId, organization, sessionId } = searchParams;
const branding = await getBrandingSettings({
serviceUrl,
organization,
});
// complete device authorization flow if device requestId is present
if (requestId && requestId.startsWith("device_")) {
const cookie = sessionId
? await getSessionCookieById({ sessionId, organization })
: await getMostRecentCookieWithLoginname({
loginName: loginName,
organization: organization,
});
await completeDeviceAuthorization(requestId.replace("device_", ""), {
sessionId: cookie.id,
sessionToken: cookie.token,
}).catch((err) => {
return (
<DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4">
<h1>{t("error.title")}</h1>
<p className="ztdl-p mb-6 block">{t("error.description")}</p>
<Alert>{err.message}</Alert>
</div>
</DynamicTheme>
);
});
}
const sessionFactors = sessionId
? await loadSessionById(serviceUrl, sessionId, organization)
: await loadMostRecentSession({
@@ -48,11 +82,6 @@ export default async function Page(props: { searchParams: Promise<any> }) {
sessionParams: { loginName, organization },
});
const branding = await getBrandingSettings({
serviceUrl,
organization,
});
let loginSettings;
if (!requestId) {
loginSettings = await getLoginSettings({
@@ -69,6 +98,13 @@ export default async function Page(props: { searchParams: Promise<any> }) {
</h1>
<p className="ztdl-p mb-6 block">{t("description")}</p>
<UserAvatar
loginName={loginName ?? sessionFactors?.factors?.user?.loginName}
displayName={sessionFactors?.factors?.user?.displayName}
showDropdown={!(requestId && requestId.startsWith("device_"))}
searchParams={searchParams}
/>
{requestId && requestId.startsWith("device_") && (
<Alert type={AlertType.INFO}>
You can now close this window and return to the device where you
@@ -76,13 +112,6 @@ export default async function Page(props: { searchParams: Promise<any> }) {
</Alert>
)}
<UserAvatar
loginName={loginName ?? sessionFactors?.factors?.user?.loginName}
displayName={sessionFactors?.factors?.user?.displayName}
showDropdown
searchParams={searchParams}
/>
{/* {sessionFactors?.id && (
<SelfServiceMenu sessionId={sessionFactors?.id} />
)} */}

View File

@@ -1,6 +1,6 @@
"use client";
import { denyDeviceAuthorization } from "@/lib/server/oidc";
import { completeDeviceAuthorization } from "@/lib/server/device";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { useRouter } from "next/navigation";
@@ -27,7 +27,9 @@ export function ConsentScreen({
async function denyDeviceAuth() {
setLoading(true);
const response = await denyDeviceAuthorization(deviceAuthorizationRequestId)
const response = await completeDeviceAuthorization(
deviceAuthorizationRequestId,
)
.catch(() => {
setError("Could not register user");
return;

View File

@@ -36,7 +36,7 @@ export function DeviceCodeForm({ userCode }: { userCode?: string }) {
const response = await getDeviceAuthorizationRequest(value.userCode)
.catch(() => {
setError("Could not complete the request");
setError("Could not continue the request");
return;
})
.finally(() => {
@@ -44,7 +44,7 @@ export function DeviceCodeForm({ userCode }: { userCode?: string }) {
});
if (!response || !response.deviceAuthorizationRequest?.id) {
setError("Could not complete the request");
setError("Could not continue the request");
return;
}

View File

@@ -0,0 +1,20 @@
"use server";
import { authorizeOrDenyDeviceAuthorization } from "@/lib/zitadel";
import { headers } from "next/headers";
import { getServiceUrlFromHeaders } from "../service";
export async function completeDeviceAuthorization(
deviceAuthorizationId: string,
session?: { sessionId: string; sessionToken: string },
) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
// without the session, device auth request is denied
return authorizeOrDenyDeviceAuthorization({
serviceUrl,
deviceAuthorizationId,
session,
});
}

View File

@@ -1,9 +1,6 @@
"use server";
import {
authorizeOrDenyDeviceAuthorization,
getDeviceAuthorizationRequest as zitadelGetDeviceAuthorizationRequest,
} from "@/lib/zitadel";
import { getDeviceAuthorizationRequest as zitadelGetDeviceAuthorizationRequest } from "@/lib/zitadel";
import { headers } from "next/headers";
import { getServiceUrlFromHeaders } from "../service";
@@ -16,14 +13,3 @@ export async function getDeviceAuthorizationRequest(userCode: string) {
userCode,
});
}
export async function denyDeviceAuthorization(deviceAuthorizationId: string) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
// without the session, device auth request is denied
return authorizeOrDenyDeviceAuthorization({
serviceUrl,
deviceAuthorizationId,
});
}

View File

@@ -952,6 +952,10 @@ export async function authorizeOrDenyDeviceAuthorization({
deviceAuthorizationId: string;
session?: { sessionId: string; sessionToken: string };
}) {
console.log("authorizeOrDenyDeviceAuthorization");
console.log("session", session);
const oidcService = await createServiceForHost(OIDCService, serviceUrl);
return oidcService.authorizeOrDenyDeviceAuthorization({