This commit is contained in:
Max Peintner
2025-05-02 17:20:28 +02:00
parent 6270cf9522
commit 54fd748b12
5 changed files with 90 additions and 32 deletions

View File

@@ -197,6 +197,11 @@
"title": "would like to connect:",
"description": "By clicking Allow, you allow this app and Zitadel to use your information in accordance with their respective terms of service and privacy policies. You can revoke this access at any time.",
"submit": "Allow"
},
"scope": {
"email": "Access your email address.",
"profile": "Access your full profile information.",
"offline_access": "Allow offline access to your account."
}
},
"error": {

View File

@@ -33,6 +33,8 @@ export default async function Page(props: {
userCode,
});
console.log(deviceAuthorizationRequest);
let defaultOrganization;
if (!organization) {
const org: Organization | null = await getDefaultOrg({
@@ -48,19 +50,28 @@ export default async function Page(props: {
organization: organization ?? defaultOrganization,
});
const params = new URLSearchParams();
if (requestId) {
params.append("requestId", requestId);
}
if (organization) {
params.append("organization", organization);
}
return (
<DynamicTheme
branding={branding}
appName={deviceAuthorizationRequest?.appName}
>
<div className="flex flex-col items-center space-y-4">
{!userCode && (
<>
<h1>{t("usercode.title")}</h1>
<p className="ztdl-p">{t("usercode.description")}</p>
<ConsentScreen scope={deviceAuthorizationRequest?.scope} />
</>
)}
<h1>{t("usercode.title")}</h1>
<p className="ztdl-p">{t("usercode.description")}</p>
<ConsentScreen
scope={deviceAuthorizationRequest?.scope}
nextUrl={`/loginname?` + params}
/>
</div>
</DynamicTheme>
);

View File

@@ -9,7 +9,6 @@ import {
createCallback,
createResponse,
getBrandingSettings,
getDeviceAuthorizationRequest,
getLoginSettings,
getSession,
} from "@/lib/zitadel";
@@ -64,24 +63,17 @@ async function loadSession(
return redirect(url);
});
} else if (requestId && requestId.startsWith("device_")) {
const userCode = requestId.replace("device_", "");
const { deviceAuthorizationRequest } = await getDeviceAuthorizationRequest({
serviceUrl,
userCode,
});
if (!deviceAuthorizationRequest) {
throw new Error("Device authorization request not found");
}
const session = {
sessionId: recent.id,
sessionToken: recent.token,
};
return authorizeOrDenyDeviceAuthorization({
serviceUrl,
deviceAuthorizationId: deviceAuthorizationRequest?.id,
session: {
sessionId: recent.id,
sessionToken: recent.token,
},
deviceAuthorizationId: requestId.replace("device_", ""),
session,
}).then(() => {
return session;
});
}
@@ -105,7 +97,11 @@ export default async function Page(props: { searchParams: Promise<any> }) {
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { loginName, requestId, organization } = searchParams;
const sessionFactors = await loadSession(serviceUrl, loginName, requestId);
// const sessionFactors = await loadSession(serviceUrl, loginName, requestId);
const sessionFactors = sessionId
? await loadSessionById(serviceUrl, sessionId, organization)
: await loadSessionByLoginname(serviceUrl, loginName, organization);
const branding = await getBrandingSettings({
serviceUrl,

View File

@@ -1,11 +1,57 @@
export function ConsentScreen({ scope }: { scope?: string[] }) {
import { useTranslations } from "next-intl";
import Link from "next/link";
import { Button, ButtonVariants } from "./button";
export function ConsentScreen({
scope,
nextUrl,
}: {
scope?: string[];
nextUrl: string;
}) {
const t = useTranslations();
return (
<div className="flex flex-col items-center space-y-4">
<h1>Consent</h1>
<p className="ztdl-p">Please confirm your consent.</p>
<div className="flex flex-col items-center space-y-4">
<button className="btn btn-primary">Accept</button>
<button className="btn btn-secondary">Reject</button>
<div className="w-full flex flex-col items-center space-y-4">
<ul className="list-disc space-y-2 w-full">
{scope?.map((s) => {
const translationKey = `device.scope.${s}`;
const description = t(translationKey, null);
// Check if the key itself is returned and provide a fallback
const resolvedDescription =
description === translationKey
? "No description available."
: description;
return (
<li
key={s}
className="grid grid-cols-4 w-full text-sm flex flex-row items-center bg-background-light-400 dark:bg-background-dark-400 border border-divider-light py-2 px-4 rounded-md transition-all"
>
<strong className="col-span-1">{s}</strong>
<span className="col-span-3">{resolvedDescription}</span>
</li>
);
})}
</ul>
<div className="mt-4 flex w-full flex-row items-center">
<Button variant={ButtonVariants.Destructive} data-testid="deny-button">
Deny
</Button>
<span className="flex-grow"></span>
<Link href={nextUrl}>
<Button
data-testid="submit-button"
type="submit"
className="self-end"
variant={ButtonVariants.Primary}
>
continue
</Button>
</Link>
</div>
</div>
);

View File

@@ -51,7 +51,7 @@ export function DeviceCodeForm({ userCode }: { userCode?: string }) {
return router.push(
`/device/consent?` +
new URLSearchParams({
requestId: `device_${userCode}`,
requestId: `device_${response.deviceAuthorizationRequest.id}`,
user_code: value.userCode,
}).toString(),
);