register with simple-lib browser (works until jwt signature validation)
This commit is contained in:
@@ -4,9 +4,13 @@ import { decode as cborDecode } from "cbor-x";
|
|||||||
import { createJWS, JWTPayload, verifyJWT } from "did-jwt";
|
import { createJWS, JWTPayload, verifyJWT } from "did-jwt";
|
||||||
import { DIDResolutionResult, Resolver } from "did-resolver";
|
import { DIDResolutionResult, Resolver } from "did-resolver";
|
||||||
import { bytesToMultibase } from "@veramo/utils";
|
import { bytesToMultibase } from "@veramo/utils";
|
||||||
import { startAuthentication } from "@simplewebauthn/browser";
|
import {
|
||||||
|
startAuthentication,
|
||||||
|
startRegistration,
|
||||||
|
} from "@simplewebauthn/browser";
|
||||||
import {
|
import {
|
||||||
generateAuthenticationOptions,
|
generateAuthenticationOptions,
|
||||||
|
generateRegistrationOptions,
|
||||||
verifyAuthenticationResponse,
|
verifyAuthenticationResponse,
|
||||||
verifyRegistrationResponse,
|
verifyRegistrationResponse,
|
||||||
VerifyRegistrationResponseOpts,
|
VerifyRegistrationResponseOpts,
|
||||||
@@ -14,6 +18,7 @@ import {
|
|||||||
import { VerifyAuthenticationResponseOpts } from "@simplewebauthn/server/esm/authentication/verifyAuthenticationResponse";
|
import { VerifyAuthenticationResponseOpts } from "@simplewebauthn/server/esm/authentication/verifyAuthenticationResponse";
|
||||||
import {
|
import {
|
||||||
Base64URLString,
|
Base64URLString,
|
||||||
|
PublicKeyCredentialCreationOptionsJSON,
|
||||||
PublicKeyCredentialRequestOptionsJSON,
|
PublicKeyCredentialRequestOptionsJSON,
|
||||||
} from "@simplewebauthn/types";
|
} from "@simplewebauthn/types";
|
||||||
|
|
||||||
@@ -36,7 +41,51 @@ function toBase64Url(anything: Uint8Array) {
|
|||||||
.replace(/=+$/, "");
|
.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,
|
userId: Uint8Array,
|
||||||
challenge: Uint8Array,
|
challenge: Uint8Array,
|
||||||
) {
|
) {
|
||||||
@@ -68,6 +117,7 @@ export async function registerCredential(
|
|||||||
const credential = await navigator.credentials.create({
|
const credential = await navigator.credentials.create({
|
||||||
publicKey: publicKeyOptions,
|
publicKey: publicKeyOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("credential", credential);
|
console.log("credential", credential);
|
||||||
console.log(credential?.id, " is the new ID base64-url-encoded");
|
console.log(credential?.id, " is the new ID base64-url-encoded");
|
||||||
console.log(toBase64Url(credential?.rawId), " is the base64 rawId");
|
console.log(toBase64Url(credential?.rawId), " is the base64 rawId");
|
||||||
@@ -288,45 +338,44 @@ export async function verifyJwt(
|
|||||||
console.log("clientAuth", clientAuth);
|
console.log("clientAuth", clientAuth);
|
||||||
|
|
||||||
const verfOpts: VerifyAuthenticationResponseOpts = {
|
const verfOpts: VerifyAuthenticationResponseOpts = {
|
||||||
response: clientAuth,
|
|
||||||
authenticator: {
|
authenticator: {
|
||||||
credentialID: credId,
|
credentialID: credId,
|
||||||
credentialPublicKey: publicKey,
|
credentialPublicKey: publicKey,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
},
|
},
|
||||||
expectedChallenge: () => true, // options.challenge doesn't work
|
expectedChallenge: options.challenge,
|
||||||
expectedOrigin: window.location.origin,
|
expectedOrigin: window.location.origin,
|
||||||
expectedRPID: window.location.hostname,
|
expectedRPID: window.location.hostname,
|
||||||
|
response: clientAuth,
|
||||||
};
|
};
|
||||||
console.log("verfOpts", verfOpts);
|
console.log("verfOpts", verfOpts);
|
||||||
const verificationFromClient = await verifyAuthenticationResponse(verfOpts);
|
const verificationFromClient = await verifyAuthenticationResponse(verfOpts);
|
||||||
console.log("client auth verification", verificationFromClient);
|
console.log("client auth verification", verificationFromClient);
|
||||||
|
|
||||||
const authData = toBase64Url(Buffer.from(authenticatorData));
|
const authData = toBase64Url(Buffer.from(authenticatorData));
|
||||||
const bufferizedJson = toBase64Url(
|
|
||||||
new TextEncoder().encode(JSON.stringify(clientDataJSON)),
|
|
||||||
);
|
|
||||||
const authOpts: VerifyAuthenticationResponseOpts = {
|
const authOpts: VerifyAuthenticationResponseOpts = {
|
||||||
response: {
|
|
||||||
id: credId,
|
|
||||||
rawId: toBase64Url(rawId),
|
|
||||||
response: {
|
|
||||||
authenticatorData: authData,
|
|
||||||
clientDataJSON: bufferizedJson,
|
|
||||||
signature: signature,
|
|
||||||
},
|
|
||||||
clientExtensionResults: {},
|
|
||||||
type: "public-key",
|
|
||||||
},
|
|
||||||
expectedChallenge: () => true, // options.challenge doesn't work
|
|
||||||
expectedOrigin: window.location.origin,
|
|
||||||
expectedRPID: window.location.hostname,
|
|
||||||
authenticator: {
|
authenticator: {
|
||||||
credentialID: credId,
|
credentialID: credId,
|
||||||
credentialPublicKey: publicKey,
|
credentialPublicKey: publicKey,
|
||||||
counter: 0,
|
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: clientAuth.response.clientDataJSON,
|
||||||
|
signature: clientAuth.response.signature,
|
||||||
|
},
|
||||||
|
type: "public-key",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
console.log("auth opts", authOpts);
|
||||||
const verification = await verifyAuthenticationResponse(authOpts);
|
const verification = await verifyAuthenticationResponse(authOpts);
|
||||||
console.log("auth verification", verification);
|
console.log("auth verification", verification);
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ import {
|
|||||||
registerCredential,
|
registerCredential,
|
||||||
verifyJwt,
|
verifyJwt,
|
||||||
} from "@/libs/didPeer";
|
} from "@/libs/didPeer";
|
||||||
|
import { Buffer } from "buffer";
|
||||||
|
|
||||||
const inputFileNameRef = ref<Blob>();
|
const inputFileNameRef = ref<Blob>();
|
||||||
|
|
||||||
@@ -260,7 +261,7 @@ export default class Help extends Vue {
|
|||||||
|
|
||||||
public async register() {
|
public async register() {
|
||||||
this.userId = generateRandomBytes(16).buffer;
|
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);
|
console.log("encodedUserId", encodedUserId);
|
||||||
|
|
||||||
const challenge = generateRandomBytes(32);
|
const challenge = generateRandomBytes(32);
|
||||||
@@ -297,7 +298,7 @@ export default class Help extends Vue {
|
|||||||
}
|
}
|
||||||
const signature = this.jwt.split(".")[2];
|
const signature = this.jwt.split(".")[2];
|
||||||
const decoded = await verifyJwt(
|
const decoded = await verifyJwt(
|
||||||
this.jwt || "",
|
this.jwt,
|
||||||
this.credId as Base64URLString,
|
this.credId as Base64URLString,
|
||||||
this.rawId as Uint8Array,
|
this.rawId as Uint8Array,
|
||||||
this.peerSetup.authenticatorData as ArrayBuffer,
|
this.peerSetup.authenticatorData as ArrayBuffer,
|
||||||
|
|||||||
Reference in New Issue
Block a user