fix webauthn flow, state

This commit is contained in:
peintnermax
2023-08-22 13:15:33 +02:00
parent ead206a188
commit 2845ad246c
12 changed files with 60 additions and 43 deletions

View File

@@ -13,7 +13,7 @@ export default async function Page({
}: {
searchParams: Record<string | number | symbol, string | undefined>;
}) {
const { loginName, altPassword } = searchParams;
const { loginName, altPassword, authRequestId } = searchParams;
const sessionFactors = await loadSession(loginName);
@@ -48,6 +48,7 @@ export default async function Page({
{loginName && (
<LoginPasskey
loginName={loginName}
authRequestId={authRequestId}
altPassword={altPassword === "true"}
/>
)}

View File

@@ -9,7 +9,7 @@ export default async function Page({
}: {
searchParams: Record<string | number | symbol, string | undefined>;
}) {
const { loginName, promptPasswordless, alt } = searchParams;
const { loginName, promptPasswordless, authRequestId, alt } = searchParams;
const sessionFactors = await loadSession(loginName);
async function loadSession(loginName?: string) {
@@ -46,6 +46,7 @@ export default async function Page({
<PasswordForm
loginName={loginName}
authRequestId={authRequestId}
promptPasswordless={promptPasswordless === "true"}
isAlternative={alt === "true"}
/>

View File

@@ -45,12 +45,11 @@ export async function POST(request: NextRequest) {
if (body) {
const { loginName, authRequestId } = body;
const domain: string = request.nextUrl.hostname;
// const domain: string = request.nextUrl.hostname;
return createSessionAndUpdateCookie(
loginName,
undefined,
domain,
undefined,
authRequestId
)

View File

@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
return createSessionAndUpdateCookie(
loginName,
password,
domain,
undefined,
undefined
).then((session) => {
return NextResponse.json(session);
@@ -44,7 +44,7 @@ export async function PUT(request: NextRequest) {
const body = await request.json();
if (body) {
const { loginName, password, challenges, passkey } = body;
const { loginName, password, challenges, passkey, authRequestId } = body;
const recentPromise: Promise<SessionCookie> = loginName
? getSessionCookieByLoginName(loginName).catch((error) => {
@@ -54,7 +54,7 @@ export async function PUT(request: NextRequest) {
return Promise.reject(error);
});
const domain: string = request.nextUrl.hostname;
// const domain: string = request.nextUrl.hostname;
return recentPromise
.then((recent) => {
@@ -64,8 +64,8 @@ export async function PUT(request: NextRequest) {
recent.loginName,
password,
passkey,
domain,
challenges
challenges,
authRequestId
).then((session) => {
return NextResponse.json({
sessionId: session.id,

View File

@@ -101,7 +101,7 @@ export async function createSession(
server: ZitadelServer,
loginName: string,
password: string | undefined,
challenges: RequestChallenges
challenges: RequestChallenges | undefined
): Promise<CreateSessionResponse | undefined> {
const sessionService = session.getSession(server);
return password
@@ -125,7 +125,7 @@ export async function setSession(
sessionToken: string,
password: string | undefined,
webAuthN: { credentialAssertionData: any } | undefined,
challenges: RequestChallenges
challenges: RequestChallenges | undefined
): Promise<SetSessionResponse | undefined> {
const sessionService = session.getSession(server);

View File

@@ -9,10 +9,15 @@ import { Spinner } from "./Spinner";
type Props = {
loginName: string;
authRequestId?: string;
altPassword: boolean;
};
export default function LoginPasskey({ loginName, altPassword }: Props) {
export default function LoginPasskey({
loginName,
authRequestId,
altPassword,
}: Props) {
const [error, setError] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false);
@@ -60,6 +65,7 @@ export default function LoginPasskey({ loginName, altPassword }: Props) {
body: JSON.stringify({
loginName,
challenges: [1], // request passkey challenge
authRequestId,
}),
});
@@ -81,6 +87,7 @@ export default function LoginPasskey({ loginName, altPassword }: Props) {
body: JSON.stringify({
loginName,
passkey: data,
authRequestId,
}),
});
@@ -168,11 +175,16 @@ export default function LoginPasskey({ loginName, altPassword }: Props) {
<Button
type="button"
variant={ButtonVariants.Secondary}
onClick={() =>
router.push(
"/password?" + new URLSearchParams({ loginName, alt: "true" }) // alt is set because password is requested as alternative auth method, so passwordless prompt can be escaped
)
}
onClick={() => {
const params = { loginName, alt: "true" };
return router.push(
"/password?" +
new URLSearchParams(
authRequestId ? { ...params, authRequestId } : params
) // alt is set because password is requested as alternative auth method, so passwordless prompt can be escaped
);
}}
>
use password
</Button>

View File

@@ -14,12 +14,14 @@ type Inputs = {
type Props = {
loginName?: string;
authRequestId?: string;
isAlternative?: boolean; // whether password was requested as alternative auth method
promptPasswordless?: boolean;
};
export default function PasswordForm({
loginName,
authRequestId,
promptPasswordless,
isAlternative,
}: Props) {
@@ -44,6 +46,7 @@ export default function PasswordForm({
body: JSON.stringify({
loginName,
password: values.password,
authRequestId,
}),
});

View File

@@ -66,6 +66,7 @@ export default function RegisterFormWithoutPassword({ legal }: Props) {
},
body: JSON.stringify({
loginName: loginName,
// authRequestId, register does not need an oidc callback at the end
}),
});

View File

@@ -39,7 +39,7 @@ export default function SessionItem({
}
const validPassword = session?.factors?.password?.verifiedAt;
const validPasskey = session?.factors?.passkey?.verifiedAt;
const validPasskey = session?.factors?.webAuthN?.verifiedAt;
const validUser = validPassword || validPasskey;

View File

@@ -79,6 +79,7 @@ export default function SetPasswordForm({
body: JSON.stringify({
loginName: loginName,
password: password,
// authRequestId, register does not need an oidc callback
}),
});

View File

@@ -65,16 +65,18 @@ export default function UsernameForm({
const method = response.authMethodTypes[0];
switch (method) {
case 1: //AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSWORD:
return router.push(
"/password?" +
new URLSearchParams(
loginSettings?.passkeysType === 1
? {
loginName: values.loginName,
promptPasswordless: `true`, // PasskeysType.PASSKEYS_TYPE_ALLOWED,
const paramsPassword: any = { loginName: values.loginName };
if (loginSettings?.passkeysType === 1) {
paramsPassword.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
}
: { loginName: values.loginName }
)
if (authRequestId) {
paramsPassword.authRequestId = authRequestId;
}
return router.push(
"/password?" + new URLSearchParams(paramsPassword)
);
case 2: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
return router.push(
@@ -82,16 +84,17 @@ export default function UsernameForm({
new URLSearchParams({ loginName: values.loginName })
);
default:
return router.push(
"/password?" +
new URLSearchParams(
loginSettings?.passkeysType === 1
? {
loginName: values.loginName,
promptPasswordless: `true`, // PasskeysType.PASSKEYS_TYPE_ALLOWED,
const paramsPasskey: any = { loginName: values.loginName };
if (loginSettings?.passkeysType === 1) {
paramsPasskey.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
}
: { loginName: values.loginName }
)
if (authRequestId) {
paramsPasskey.authRequestId = authRequestId;
}
return router.push(
"/password?" + new URLSearchParams(paramsPasskey)
);
}
} else if (

View File

@@ -4,19 +4,17 @@ import {
addSessionToCookie,
updateSessionCookie,
} from "./cookies";
import { ChallengeKind, Session, Challenges } from "@zitadel/server";
import { Session, Challenges, RequestChallenges } from "@zitadel/server";
export async function createSessionAndUpdateCookie(
loginName: string,
password: string | undefined,
domain: string,
challenges: ChallengeKind[] | undefined,
challenges: RequestChallenges | undefined,
authRequestId: string | undefined
): Promise<Session> {
const createdSession = await createSession(
server,
loginName,
domain,
password,
challenges
);
@@ -61,15 +59,13 @@ export async function setSessionAndUpdateCookie(
loginName: string,
password: string | undefined,
passkey: { credentialAssertionData: any } | undefined,
domain: string | undefined,
challenges: ChallengeKind[] | undefined,
challenges: RequestChallenges | undefined,
authRequestId: string | undefined
): Promise<SessionWithChallenges> {
return setSession(
server,
sessionId,
sessionToken,
domain,
password,
passkey,
challenges