mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 11:17:32 +00:00
device code flow
This commit is contained in:
@@ -190,7 +190,7 @@
|
||||
"device": {
|
||||
"usercode": {
|
||||
"title": "Device code",
|
||||
"description": "Enter the code provided in the verification email.",
|
||||
"description": "Enter the code.",
|
||||
"submit": "Continue"
|
||||
},
|
||||
"request": {
|
||||
|
@@ -5,9 +5,11 @@ import { UserAvatar } from "@/components/user-avatar";
|
||||
import { getMostRecentCookieWithLoginname } from "@/lib/cookies";
|
||||
import { getServiceUrlFromHeaders } from "@/lib/service";
|
||||
import {
|
||||
authorizeOrDenyDeviceAuthorization,
|
||||
createCallback,
|
||||
createResponse,
|
||||
getBrandingSettings,
|
||||
getDeviceAuthorizationRequest,
|
||||
getLoginSettings,
|
||||
getSession,
|
||||
} from "@/lib/zitadel";
|
||||
@@ -24,7 +26,6 @@ import { redirect } from "next/navigation";
|
||||
|
||||
async function loadSession(
|
||||
serviceUrl: string,
|
||||
|
||||
loginName: string,
|
||||
requestId?: string,
|
||||
) {
|
||||
@@ -62,6 +63,26 @@ async function loadSession(
|
||||
}).then(({ url }) => {
|
||||
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");
|
||||
}
|
||||
|
||||
return authorizeOrDenyDeviceAuthorization({
|
||||
serviceUrl,
|
||||
deviceAuthorizationId: deviceAuthorizationRequest?.id,
|
||||
session: {
|
||||
sessionId: recent.id,
|
||||
sessionToken: recent.token,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return getSession({
|
||||
|
@@ -127,6 +127,7 @@ export async function GET(request: NextRequest) {
|
||||
request,
|
||||
});
|
||||
} else if (requestId.startsWith("device_")) {
|
||||
// this finishes the login process for Device Authorization
|
||||
return loginWithDeviceAndSession({
|
||||
serviceUrl,
|
||||
deviceRequest: requestId.replace("device_", ""),
|
||||
@@ -509,7 +510,9 @@ export async function GET(request: NextRequest) {
|
||||
requestId: `saml_${samlRequest.id}`,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}
|
||||
// Device Authorization does not need to start here as it is handled on the /device endpoint
|
||||
else {
|
||||
return NextResponse.json(
|
||||
{ error: "No authRequest nor samlRequest provided" },
|
||||
{ status: 500 },
|
||||
|
@@ -51,7 +51,7 @@ export function DeviceCodeForm({ userCode }: { userCode?: string }) {
|
||||
return router.push(
|
||||
`/device/consent?` +
|
||||
new URLSearchParams({
|
||||
requestId: `device_${response.deviceAuthorizationRequest.id}`,
|
||||
requestId: `device_${userCode}`,
|
||||
user_code: value.userCode,
|
||||
}).toString(),
|
||||
);
|
||||
|
@@ -5,6 +5,28 @@ type FinishFlowCommand =
|
||||
}
|
||||
| { loginName: string };
|
||||
|
||||
function goToSignedInPage(
|
||||
props:
|
||||
| { sessionId: string; organization?: string }
|
||||
| { organization?: string; loginName: string },
|
||||
) {
|
||||
const params = new URLSearchParams({});
|
||||
|
||||
if ("loginName" in props && props.loginName) {
|
||||
params.append("loginName", props.loginName);
|
||||
}
|
||||
|
||||
if ("sessionId" in props && props.sessionId) {
|
||||
params.append("sessionId", props.sessionId);
|
||||
}
|
||||
|
||||
if (props.organization) {
|
||||
params.append("organization", props.organization);
|
||||
}
|
||||
|
||||
return `/signedin?` + params;
|
||||
}
|
||||
|
||||
/**
|
||||
* for client: redirects user back to an OIDC or SAML application or to a success page when using requestId, check if a default redirect and redirect to it, or just redirect to a success page with the loginName
|
||||
* @param command
|
||||
@@ -14,7 +36,25 @@ export async function getNextUrl(
|
||||
command: FinishFlowCommand & { organization?: string },
|
||||
defaultRedirectUri?: string,
|
||||
): Promise<string> {
|
||||
if ("sessionId" in command && "requestId" in command) {
|
||||
// finish Device Authorization Flow
|
||||
if (
|
||||
"requestId" in command &&
|
||||
command.requestId.startsWith("device_") &&
|
||||
("loginName" in command || "sessionId" in command)
|
||||
) {
|
||||
return goToSignedInPage({
|
||||
...command,
|
||||
organization: command.organization,
|
||||
});
|
||||
}
|
||||
|
||||
// finish SAML or OIDC flow
|
||||
if (
|
||||
"sessionId" in command &&
|
||||
"requestId" in command &&
|
||||
(command.requestId.startsWith("saml_") ||
|
||||
command.requestId.startsWith("oidc_"))
|
||||
) {
|
||||
const params = new URLSearchParams({
|
||||
sessionId: command.sessionId,
|
||||
requestId: command.requestId,
|
||||
@@ -31,13 +71,5 @@ export async function getNextUrl(
|
||||
return defaultRedirectUri;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
loginName: command.loginName,
|
||||
});
|
||||
|
||||
if (command.organization) {
|
||||
params.append("organization", command.organization);
|
||||
}
|
||||
|
||||
return `/signedin?` + params;
|
||||
return goToSignedInPage(command);
|
||||
}
|
||||
|
Reference in New Issue
Block a user