navigator, utils

This commit is contained in:
Max Peintner
2023-06-13 17:43:37 +02:00
parent 084051889b
commit 69301d789d
5 changed files with 195 additions and 19 deletions

View File

@@ -1,6 +1,7 @@
import { import {
createPasskeyRegistrationLink, createPasskeyRegistrationLink,
getSession, getSession,
registerPasskey,
server, server,
} from "#/lib/zitadel"; } from "#/lib/zitadel";
import { getSessionCookieById } from "#/utils/cookies"; import { getSessionCookieById } from "#/utils/cookies";
@@ -12,7 +13,6 @@ export async function POST(request: NextRequest) {
const { sessionId } = body; const { sessionId } = body;
const sessionCookie = await getSessionCookieById(sessionId); const sessionCookie = await getSessionCookieById(sessionId);
console.log(sessionCookie);
const session = await getSession( const session = await getSession(
server, server,
@@ -20,14 +20,15 @@ export async function POST(request: NextRequest) {
sessionCookie.token sessionCookie.token
); );
if (session?.session && session.session?.factors?.user?.id) { const userId = session?.session?.factors?.user?.id;
console.log(session.session.factors.user.id, sessionCookie.token);
return createPasskeyRegistrationLink( if (userId) {
session.session.factors.user.id, return createPasskeyRegistrationLink(userId, sessionCookie.token)
sessionCookie.token
)
.then((resp) => { .then((resp) => {
return NextResponse.json(resp); const code = resp.code;
return registerPasskey(userId, code).then((resp) => {
return NextResponse.json(resp);
});
}) })
.catch((error) => { .catch((error) => {
return NextResponse.json(error, { status: 500 }); return NextResponse.json(error, { status: 500 });

View File

@@ -251,7 +251,7 @@ export async function verifyPasskeyRegistration(
*/ */
export async function registerPasskey( export async function registerPasskey(
userId: string, userId: string,
sessionToken: string code: { id: string; code: string }
): Promise<any> { ): Promise<any> {
// this actions will be made from the currently seleected user // this actions will be made from the currently seleected user
const zitadelConfig: ZitadelServerOptions = { const zitadelConfig: ZitadelServerOptions = {
@@ -263,13 +263,11 @@ export async function registerPasskey(
const authserver: ZitadelServer = initializeServer(zitadelConfig); const authserver: ZitadelServer = initializeServer(zitadelConfig);
console.log("server", authserver); console.log("server", authserver);
const userservice = user.getUser(server); const userservice = user.getUser(server);
return userservice.registerPasskey( return userservice.registerPasskey({
{ userId,
userId, code,
// returnCode: new ReturnPasskeyRegistrationCode(), // returnCode: new ReturnPasskeyRegistrationCode(),
}, });
{ metadata: bearerTokenMetadata(sessionToken) }
);
} }
export { server }; export { server };

View File

@@ -6,7 +6,8 @@ import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { Spinner } from "./Spinner"; import { Spinner } from "./Spinner";
import Alert from "./Alert"; import Alert from "./Alert";
import { RegisterPasskeyResponse } from "@zitadel/server";
import { coerceToArrayBuffer, coerceToBase64Url } from "#/utils/base64";
type Inputs = {}; type Inputs = {};
type Props = { type Props = {
@@ -50,8 +51,120 @@ export default function RegisterPasskey({ sessionId }: Props) {
} }
function submitRegisterAndContinue(value: Inputs): Promise<boolean | void> { function submitRegisterAndContinue(value: Inputs): Promise<boolean | void> {
return submitRegister().then((resp: any) => { return submitRegister().then((resp: RegisterPasskeyResponse) => {
return router.push(`/accounts`); console.log(resp.publicKeyCredentialCreationOptions?.publicKey);
if (
resp.publicKeyCredentialCreationOptions &&
resp.publicKeyCredentialCreationOptions.publicKey
) {
resp.publicKeyCredentialCreationOptions.publicKey.challenge =
coerceToArrayBuffer(
resp.publicKeyCredentialCreationOptions.publicKey.challenge,
"challenge"
);
resp.publicKeyCredentialCreationOptions.publicKey.user.id =
coerceToArrayBuffer(
resp.publicKeyCredentialCreationOptions.publicKey.user.id,
"challenge"
);
if (
resp.publicKeyCredentialCreationOptions.publicKey.excludeCredentials
) {
resp.publicKeyCredentialCreationOptions.publicKey.excludeCredentials.map(
(cred: any) => {
cred.id = coerceToArrayBuffer(
cred.id as string,
"excludeCredentials.id"
);
return cred;
}
);
}
navigator.credentials
.create(resp.publicKeyCredentialCreationOptions)
.then((resp) => {
console.log(resp);
if (
resp &&
(resp as any).response.attestationObject &&
(resp as any).response.clientDataJSON &&
(resp as any).rawId
) {
const attestationObject = (resp as any).response
.attestationObject;
const clientDataJSON = (resp as any).response.clientDataJSON;
const rawId = (resp as any).rawId;
const data = JSON.stringify({
id: resp.id,
rawId: coerceToBase64Url(rawId, "rawId"),
type: resp.type,
response: {
attestationObject: coerceToBase64Url(
attestationObject,
"attestationObject"
),
clientDataJSON: coerceToBase64Url(
clientDataJSON,
"clientDataJSON"
),
},
});
const base64 = btoa(data);
return base64;
// if (this.type === U2FComponentDestination.MFA) {
// this.service
// .verifyMyMultiFactorU2F(base64, this.name)
// .then(() => {
// this.translate
// .get("USER.MFA.U2F_SUCCESS")
// .pipe(take(1))
// .subscribe((msg) => {
// this.toast.showInfo(msg);
// });
// this.dialogRef.close(true);
// this.loading = false;
// })
// .catch((error) => {
// this.loading = false;
// this.toast.showError(error);
// });
// } else if (this.type === U2FComponentDestination.PASSWORDLESS) {
// this.service
// .verifyMyPasswordless(base64, this.name)
// .then(() => {
// this.translate
// .get("USER.PASSWORDLESS.U2F_SUCCESS")
// .pipe(take(1))
// .subscribe((msg) => {
// this.toast.showInfo(msg);
// });
// this.dialogRef.close(true);
// this.loading = false;
// })
// .catch((error) => {
// this.loading = false;
// this.toast.showError(error);
// });
// }
} else {
setLoading(false);
setError("An error on registering passkey");
return null;
}
})
.catch((error) => {
console.error(error);
setLoading(false);
// setError(error);
return null;
});
}
// return router.push(`/accounts`);
}); });
} }

View File

@@ -0,0 +1,63 @@
export function coerceToBase64Url(thing: any, name: string) {
// Array or ArrayBuffer to Uint8Array
if (Array.isArray(thing)) {
thing = Uint8Array.from(thing);
}
if (thing instanceof ArrayBuffer) {
thing = new Uint8Array(thing);
}
// Uint8Array to base64
if (thing instanceof Uint8Array) {
var str = "";
var len = thing.byteLength;
for (var i = 0; i < len; i++) {
str += String.fromCharCode(thing[i]);
}
thing = window.btoa(str);
}
if (typeof thing !== "string") {
throw new Error("could not coerce '" + name + "' to string");
}
// base64 to base64url
// NOTE: "=" at the end of challenge is optional, strip it off here
thing = thing.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, "");
return thing;
}
export function coerceToArrayBuffer(thing: any, name: string) {
if (typeof thing === "string") {
// base64url to base64
thing = thing.replace(/-/g, "+").replace(/_/g, "/");
// base64 to Uint8Array
var str = window.atob(thing);
var bytes = new Uint8Array(str.length);
for (var i = 0; i < str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
thing = bytes;
}
// Array to Uint8Array
if (Array.isArray(thing)) {
thing = new Uint8Array(thing);
}
// Uint8Array to ArrayBuffer
if (thing instanceof Uint8Array) {
thing = thing.buffer;
}
// error if none of the above worked
if (!(thing instanceof ArrayBuffer)) {
throw new TypeError("could not coerce '" + name + "' to ArrayBuffer");
}
return thing;
}

View File

@@ -29,6 +29,7 @@ export {
AddHumanUserResponse, AddHumanUserResponse,
VerifyEmailResponse, VerifyEmailResponse,
VerifyPasskeyRegistrationResponse, VerifyPasskeyRegistrationResponse,
RegisterPasskeyResponse,
} from "./proto/server/zitadel/user/v2alpha/user_service"; } from "./proto/server/zitadel/user/v2alpha/user_service";
export { type LegalAndSupportSettings } from "./proto/server/zitadel/settings/v2alpha/legal_settings"; export { type LegalAndSupportSettings } from "./proto/server/zitadel/settings/v2alpha/legal_settings";