diff --git a/src/libs/didPeer.ts b/src/libs/didPeer.ts index 6672677..6cb4e9c 100644 --- a/src/libs/didPeer.ts +++ b/src/libs/didPeer.ts @@ -4,9 +4,13 @@ import { decode as cborDecode } from "cbor-x"; import { createJWS, JWTPayload, verifyJWT } from "did-jwt"; import { DIDResolutionResult, Resolver } from "did-resolver"; import { bytesToMultibase } from "@veramo/utils"; -import { startAuthentication } from "@simplewebauthn/browser"; +import { + startAuthentication, + startRegistration, +} from "@simplewebauthn/browser"; import { generateAuthenticationOptions, + generateRegistrationOptions, verifyAuthenticationResponse, verifyRegistrationResponse, VerifyRegistrationResponseOpts, @@ -14,6 +18,7 @@ import { import { VerifyAuthenticationResponseOpts } from "@simplewebauthn/server/esm/authentication/verifyAuthenticationResponse"; import { Base64URLString, + PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, } from "@simplewebauthn/types"; @@ -36,7 +41,51 @@ function toBase64Url(anything: Uint8Array) { .replace(/=+$/, ""); } -export async function registerCredential( +export async function registerCredential() { + const options: PublicKeyCredentialCreationOptionsJSON = + await generateRegistrationOptions({ + rpName: "Time Safari", + rpID: window.location.hostname, + userName: "Current-User", + // Don't prompt users for additional information about the authenticator + // (Recommended for smoother UX) + attestationType: "none", + // Prevent users from re-registering existing authenticators + // excludeCredentials: userPasskeys.map(passkey => ({ + // id: passkey.id, + // // Optional + // transports: passkey.transports, + // })), + // // See "Guiding use of authenticators via authenticatorSelection" below + authenticatorSelection: { + // Defaults + residentKey: "preferred", + userVerification: "preferred", + // Optional + authenticatorAttachment: "platform", + }, + }); + const attResp = await startRegistration(options); + console.log("attResp", attResp); + const verification = await verifyRegistrationResponse({ + response: attResp, + expectedChallenge: options.challenge, + expectedOrigin: window.location.origin, + expectedRPID: window.location.hostname, + }); + console.log("verification", verification); + + return { + authData: verification.registrationInfo?.attestationObject, + credId: verification.registrationInfo?.credentialID as string, + rawId: new Uint8Array(new Buffer(attResp.rawId, "base64")), + publicKeyJwk: undefined, + publicKeyBytes: verification.registrationInfo + ?.credentialPublicKey as Uint8Array, + }; +} + +export async function registerCredential2( userId: Uint8Array, challenge: Uint8Array, ) { @@ -68,6 +117,7 @@ export async function registerCredential( const credential = await navigator.credentials.create({ publicKey: publicKeyOptions, }); + console.log("credential", credential); console.log(credential?.id, " is the new ID base64-url-encoded"); console.log(toBase64Url(credential?.rawId), " is the base64 rawId"); @@ -288,45 +338,44 @@ export async function verifyJwt( console.log("clientAuth", clientAuth); const verfOpts: VerifyAuthenticationResponseOpts = { - response: clientAuth, authenticator: { credentialID: credId, credentialPublicKey: publicKey, counter: 0, }, - expectedChallenge: () => true, // options.challenge doesn't work + expectedChallenge: options.challenge, expectedOrigin: window.location.origin, expectedRPID: window.location.hostname, + response: clientAuth, }; console.log("verfOpts", verfOpts); const verificationFromClient = await verifyAuthenticationResponse(verfOpts); console.log("client auth verification", verificationFromClient); const authData = toBase64Url(Buffer.from(authenticatorData)); - const bufferizedJson = toBase64Url( - new TextEncoder().encode(JSON.stringify(clientDataJSON)), - ); const authOpts: VerifyAuthenticationResponseOpts = { + authenticator: { + credentialID: credId, + credentialPublicKey: publicKey, + counter: 0, + }, + expectedChallenge: options.challenge, + expectedOrigin: window.location.origin, + expectedRPID: window.location.hostname, response: { + authenticatorAttachment: "platform", + clientExtensionResults: {}, id: credId, rawId: toBase64Url(rawId), response: { authenticatorData: authData, - clientDataJSON: bufferizedJson, - signature: signature, + clientDataJSON: clientAuth.response.clientDataJSON, + signature: clientAuth.response.signature, }, - clientExtensionResults: {}, type: "public-key", }, - expectedChallenge: () => true, // options.challenge doesn't work - expectedOrigin: window.location.origin, - expectedRPID: window.location.hostname, - authenticator: { - credentialID: credId, - credentialPublicKey: publicKey, - counter: 0, - }, }; + console.log("auth opts", authOpts); const verification = await verifyAuthenticationResponse(authOpts); console.log("auth verification", verification); diff --git a/src/views/TestView.vue b/src/views/TestView.vue index 75112fd..5f92271 100644 --- a/src/views/TestView.vue +++ b/src/views/TestView.vue @@ -209,6 +209,7 @@ import { registerCredential, verifyJwt, } from "@/libs/didPeer"; +import { Buffer } from "buffer"; const inputFileNameRef = ref(); @@ -260,7 +261,7 @@ export default class Help extends Vue { public async register() { this.userId = generateRandomBytes(16).buffer; - const encodedUserId = new TextDecoder("utf-8").decode(this.userId); + const encodedUserId = Buffer.from(this.userId).toString("base64"); console.log("encodedUserId", encodedUserId); const challenge = generateRandomBytes(32); @@ -297,7 +298,7 @@ export default class Help extends Vue { } const signature = this.jwt.split(".")[2]; const decoded = await verifyJwt( - this.jwt || "", + this.jwt, this.credId as Base64URLString, this.rawId as Uint8Array, this.peerSetup.authenticatorData as ArrayBuffer,