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": { "signedin": {
"title": "Willkommen {user}!", "title": "Willkommen {user}!",
"description": "Sie sind angemeldet.", "description": "Sie sind angemeldet.",
"continue": "Weiter" "continue": "Weiter",
"error": {
"title": "Fehler",
"description": "Ein Fehler ist aufgetreten."
}
}, },
"verify": { "verify": {
"userIdMissing": "Keine Benutzer-ID angegeben!", "userIdMissing": "Keine Benutzer-ID angegeben!",

View File

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

View File

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

View File

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

View File

@@ -166,7 +166,11 @@
"signedin": { "signedin": {
"title": "Witaj {user}!", "title": "Witaj {user}!",
"description": "Jesteś zalogowany.", "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": { "verify": {
"userIdMissing": "Nie podano identyfikatora użytkownika!", "userIdMissing": "Nie podano identyfikatora użytkownika!",

View File

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

View File

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

View File

@@ -2,7 +2,11 @@ import { Alert, AlertType } from "@/components/alert";
import { Button, ButtonVariants } from "@/components/button"; import { Button, ButtonVariants } from "@/components/button";
import { DynamicTheme } from "@/components/dynamic-theme"; import { DynamicTheme } from "@/components/dynamic-theme";
import { UserAvatar } from "@/components/user-avatar"; 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 { getServiceUrlFromHeaders } from "@/lib/service";
import { loadMostRecentSession } from "@/lib/session"; import { loadMostRecentSession } from "@/lib/session";
import { import {
@@ -41,6 +45,36 @@ export default async function Page(props: { searchParams: Promise<any> }) {
const { loginName, requestId, organization, sessionId } = searchParams; 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 const sessionFactors = sessionId
? await loadSessionById(serviceUrl, sessionId, organization) ? await loadSessionById(serviceUrl, sessionId, organization)
: await loadMostRecentSession({ : await loadMostRecentSession({
@@ -48,11 +82,6 @@ export default async function Page(props: { searchParams: Promise<any> }) {
sessionParams: { loginName, organization }, sessionParams: { loginName, organization },
}); });
const branding = await getBrandingSettings({
serviceUrl,
organization,
});
let loginSettings; let loginSettings;
if (!requestId) { if (!requestId) {
loginSettings = await getLoginSettings({ loginSettings = await getLoginSettings({
@@ -69,6 +98,13 @@ export default async function Page(props: { searchParams: Promise<any> }) {
</h1> </h1>
<p className="ztdl-p mb-6 block">{t("description")}</p> <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_") && ( {requestId && requestId.startsWith("device_") && (
<Alert type={AlertType.INFO}> <Alert type={AlertType.INFO}>
You can now close this window and return to the device where you 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> </Alert>
)} )}
<UserAvatar
loginName={loginName ?? sessionFactors?.factors?.user?.loginName}
displayName={sessionFactors?.factors?.user?.displayName}
showDropdown
searchParams={searchParams}
/>
{/* {sessionFactors?.id && ( {/* {sessionFactors?.id && (
<SelfServiceMenu sessionId={sessionFactors?.id} /> <SelfServiceMenu sessionId={sessionFactors?.id} />
)} */} )} */}

View File

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

View File

@@ -36,7 +36,7 @@ export function DeviceCodeForm({ userCode }: { userCode?: string }) {
const response = await getDeviceAuthorizationRequest(value.userCode) const response = await getDeviceAuthorizationRequest(value.userCode)
.catch(() => { .catch(() => {
setError("Could not complete the request"); setError("Could not continue the request");
return; return;
}) })
.finally(() => { .finally(() => {
@@ -44,7 +44,7 @@ export function DeviceCodeForm({ userCode }: { userCode?: string }) {
}); });
if (!response || !response.deviceAuthorizationRequest?.id) { if (!response || !response.deviceAuthorizationRequest?.id) {
setError("Could not complete the request"); setError("Could not continue the request");
return; 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"; "use server";
import { import { getDeviceAuthorizationRequest as zitadelGetDeviceAuthorizationRequest } from "@/lib/zitadel";
authorizeOrDenyDeviceAuthorization,
getDeviceAuthorizationRequest as zitadelGetDeviceAuthorizationRequest,
} from "@/lib/zitadel";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { getServiceUrlFromHeaders } from "../service"; import { getServiceUrlFromHeaders } from "../service";
@@ -16,14 +13,3 @@ export async function getDeviceAuthorizationRequest(userCode: string) {
userCode, 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; deviceAuthorizationId: string;
session?: { sessionId: string; sessionToken: string }; session?: { sessionId: string; sessionToken: string };
}) { }) {
console.log("authorizeOrDenyDeviceAuthorization");
console.log("session", session);
const oidcService = await createServiceForHost(OIDCService, serviceUrl); const oidcService = await createServiceForHost(OIDCService, serviceUrl);
return oidcService.authorizeOrDenyDeviceAuthorization({ return oidcService.authorizeOrDenyDeviceAuthorization({