|
@ -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 = { |
|
|
|
|
|
authenticator: { |
|
|
|
|
|
credentialID: credId, |
|
|
|
|
|
credentialPublicKey: publicKey, |
|
|
|
|
|
counter: 0, |
|
|
|
|
|
}, |
|
|
|
|
|
expectedChallenge: options.challenge, |
|
|
|
|
|
expectedOrigin: window.location.origin, |
|
|
|
|
|
expectedRPID: window.location.hostname, |
|
|
response: { |
|
|
response: { |
|
|
|
|
|
authenticatorAttachment: "platform", |
|
|
|
|
|
clientExtensionResults: {}, |
|
|
id: credId, |
|
|
id: credId, |
|
|
rawId: toBase64Url(rawId), |
|
|
rawId: toBase64Url(rawId), |
|
|
response: { |
|
|
response: { |
|
|
authenticatorData: authData, |
|
|
authenticatorData: authData, |
|
|
clientDataJSON: bufferizedJson, |
|
|
clientDataJSON: clientAuth.response.clientDataJSON, |
|
|
signature: signature, |
|
|
signature: clientAuth.response.signature, |
|
|
}, |
|
|
}, |
|
|
clientExtensionResults: {}, |
|
|
|
|
|
type: "public-key", |
|
|
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); |
|
|
const verification = await verifyAuthenticationResponse(authOpts); |
|
|
console.log("auth verification", verification); |
|
|
console.log("auth verification", verification); |
|
|
|
|
|
|
|
|