New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deno support #268
Comments
What is currently broken about trying to use SimpleWebAuthn in a Deno project? If there's an error raised in your project then please provide a reproduction I can use on my end to recreate and begin strategizing a fix. |
I mean publishing it on deno.land/x: https://deno.land/manual@main/publishing |
|
Isn't there a native equivalent in the std io library? https://deno.land/std@0.76.0/node/buffer.ts I had a similar issue where I was serializing a I basically had to write my own implementation of "serialized buffer to actual buffer" - if the underlying library used an array (which i feel like is Data Structures 101, every language should have that, right?) the actual distribution of that library for Node could have a built-in serializer/deserializer that used the One other route that I see somewhere in the code is that the Node buffer got base64'd into a string, then decoded where needed. Maybe that's an option? Though I imagine that might be slower overall... |
I wonder if there's a performance benefit to be had from dropping all the serialization for "logic that handles serialized representations." Back when I started this project I was not confident enough in using
Some random Quora post made this connection for me the other day, and so I think you're right (in the related Discussion) that refactoring to use a more widely available type like |
The thought just occurred to me: import paths to standard lib functionality in Node and Deno as so completely different, how do I detect the availability of API's like
So now I'm not sure what the goal should be. Would each runtime need its own package? Do I try to build and maintain helper methods to hide away where the method gets imported from? And how would TypeScript handle such a thing? 馃槺 Gotta think on this more. |
Someone showed me a trick like this:
As long as the Someone should write a node<->deno transpiler. :D |
Deno have global
But I do not verify attestation, just signature. Maybe runtime crypto will be not enough for attestation verification, and you need this package. For node it is really a problem, cause node do not have |
Today's discovery: the The library doesn't know how to handle |
cbor-x should handle both: https://github.com/kriszyp/cbor-x/blob/master/encode.js#L8 also I think it is possible to implement passkeys without any package at all with |
Is anyone around who's well-versed in SubtleCrypto? I'm in the final stages of this "isomorphic" refactor (see #299) and have managed to replace all but one use of Node's const { createVerify, webcrypto } = import('node:crypto');
const target = {
hashAlgorithm: 'sha256',
publicKeyPEM: '-----BEGIN PUBLIC KEY-----\n' +
'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPzMMB0nPKu9zvu6tvvyaP7MlGKJi\n' +
'4zazYQw5kyCjGymyHxcnMCwcj4llYwRY+MedgOCQzcz/TgKeabY4yFQyrA==\n' +
'-----END PUBLIC KEY-----',
publicKey: 'a50102032620012158203f330c0749cf2aef73beeeadbefc9a3fb32518a262e336b3610c399320a31b29225820b21f1727302c1c8f8965630458f8c79d80e090cdccff4e029e69b638c85432ac',
publicKeyDER: '3059301306072a8648ce3d020106082a8648ce3d030107034200043f330c0749cf2aef73beeeadbefc9a3fb32518a262e336b3610c399320a31b29b21f1727302c1c8f8965630458f8c79d80e090cdccff4e029e69b638c85432ac',
signatureBase: '49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97634561f598a8adce000235bcc60a648b0b25f1f05503004c014cb00ec3d810ea2e708dfea3e8c1d49e8ab7a400c4b31ad56c052a3d5b362e2901a91602c657fa92788007839a870bed25e797cbf351f2e47eb38621599db354618d4d436085a5c5c1a100a50102032620012158203f330c0749cf2aef73beeeadbefc9a3fb32518a262e336b3610c399320a31b29225820b21f1727302c1c8f8965630458f8c79d80e090cdccff4e029e69b638c85432ac6fd7c1a810d55544a222c0e22ea57a8687d09dda5fc5f614c5faddeb8b91cb61',
signature: '3045022100f828cb7b3121e52f37d328f2dc322106fd8570c7c50daeac8684ab5e2eeaf34702207c96d9428a9a6c30822c87a02a328665507fc11f46d74f5c6c376ccaa02132ef'
};
(async () => {
const { hashAlgorithm, publicKeyDER, publicKeyPEM, signature, signatureBase } = target;
const publicKeyDERBytes = Buffer.from(publicKeyDER, 'hex');
const signatureBytes = Buffer.from(signature, 'hex');
const signatureBaseBytes = Buffer.from(signatureBase, 'hex');
/**
* Attempting to make things work with the SubtleCrypto Web API
*
* https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
* https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/verify
*/
const subtleKey = await webcrypto.subtle.importKey(
"spki",
publicKeyDERBytes,
{ name: 'ECDSA', namedCurve: "P-256" },
false,
["verify"]
);
// SubtleCrypto
console.log(
await webcrypto.subtle.verify(
{ name: 'ECDSA', hash: { name: 'SHA-256' } },
subtleKey,
signatureBytes,
signatureBaseBytes,
)
); // false
/**
* Using Node's `crypto.createVerify()`
*
* https://nodejs.org/api/crypto.html#verifyverifyobject-signature-signatureencoding
*/
console.log(
createVerify(hashAlgorithm)
.update(signatureBaseBytes)
.verify(publicKeyPEM, signatureBytes)
); // true
})(); If someone is able to help out here I'd be grateful, there's not a lot to go off of than MDN's examples of SubtleCrypto's methods and I'm kinda stumped. Edit: After a bit of experimenting I think the const subtleKey = await webcrypto.subtle.importKey(
"jwk",
{
kty: 'EC',
crv: 'P-256',
x: 'PzMMB0nPKu9zvu6tvvyaP7MlGKJi4zazYQw5kyCjGyk',
y: 'sh8XJzAsHI-JZWMEWPjHnYDgkM3M_04Cnmm2OMhUMqw',
ext: true
},
{
name: 'ECDSA',
namedCurve: "P-256",
},
true,
["verify"]
);
// SubtleCrypto
console.log(
await webcrypto.subtle.verify(
{
name: 'ECDSA',
hash: { name: 'SHA-256' },
},
subtleKey,
signatureBytes.buffer,
signatureBaseBytes.buffer,
)
); // false |
Hey @linuxwolf, thank you for the assist! You were right, it was the case of my needing to peel apart the DER-encoded R and S (from code I'm working on in #299):
Once I figured that out RSA support using SubtleCrypto followed pretty quickly as it didn't involve anything unique like this 馃帀 |
#299 is in a really good place right now. The code rewrite itself is largely done, and now I want to test it out in some alternative runtimes like Deno and CloudFlare Workers. Unfortunately, Deno has already proven to be a tough thing to even get a simple script running. I can't use 1.28's new So at this point I'm thinking it's time to start outputting an ESM build of @simplewebauthn/server too, alongside the CommonJS one for Node compatibility. I already use Rollup for @simplewebauthn/browser so I'll start playing around with that. Perhaps there's an extension for Rollup that can take care of adding file extensions after compiling to ESM? In the meantime, if any of you subscribed to this issue have tips on how best to build the project to appease Deno's requirements, perhaps you can point me in the right direction? There are many variables I can think of that I'll end up manipulating through trial and error until something builds that might be suitable for environments that only support ESM. If someone has expertise in this project I'd be grateful for any time you could save me on the next step of this endeavor. For now I'll play around with Rollup to output ESM-compatible code (i.e. |
Note to self: I wonder if something like this might work, rewrite to work in Deno and compile to Node instead: https://github.com/fromdeno/deno2node From denoland/deno#9569 |
Last night I got server to run in a CloudFlare Worker without needing to make any changes - it seems CloudFlare workers support CommonJS modules? I couldn't find anything to corroborate that, but hey, it works: And it's bordering on ridiculous, but for fun I also managed to get server running in a Vite + React project. I had to temporarily switch two packages to build to ESNext modules, though, which would negatively impacts Node support: I'm kinda wondering what to make of this exploration into how to get server running in more ESM-first places. Ssetting aside front-end browser support, I want SimpleWebAuthn to support more ESM-first environments. However to do so it seems I might need to generate two builds in dist/ (one "main": "dist/node.js",
"types": "./dist/node.d.ts",
"exports": {
"node": "./dist/node.js",
"default": "./dist/esm.js"
}, Honestly I should probably punt on that part of things for now, and take the win that the code in #299 is largely completely decoupled from any Node-specific APIs. That'd set a more solid foundation for subsequent work to figure out how else I might support non-Node runtimes. |
I think I can close this out now. I just a quick test with Deno 1.28 and 1.29 using the latest @simplewebauthn/server@7.0.0, and the code actually ran! import { generateRegistrationOptions } from 'npm:@simplewebauthn/server@7.0.0';
(async () => {
const rpName = 'SimpleWebAuthn (Deno)';
const rpID = 'not.real';
const userID = '1234';
const userName = 'usernameHere';
const timeout = 1;
const attestationType = 'indirect';
console.log('Calling generateRegistrationOptions()');
const options = await generateRegistrationOptions({
rpName,
rpID,
userID,
userName,
timeout,
attestationType,
});
console.log(options);
})(); $> deno run --allow-read --allow-env --allow-ffi --unstable ./index.ts
Calling generateRegistrationOptions()
{
challenge: "_SkQCFNKrbhIWQvuKDIpS45-m6fTkW1EG7BwrUbAmjk",
rp: { name: "SimpleWebAuthn (Deno)", id: "not.real" },
user: { id: "1234", name: "usernameHere", displayName: "usernameHere" },
pubKeyCredParams: [
{ alg: -8, type: "public-key" },
{ alg: -7, type: "public-key" },
{ alg: -36, type: "public-key" },
{ alg: -37, type: "public-key" },
{ alg: -38, type: "public-key" },
{ alg: -39, type: "public-key" },
{ alg: -257, type: "public-key" },
{ alg: -258, type: "public-key" },
{ alg: -259, type: "public-key" }
],
timeout: 1,
attestation: "indirect",
excludeCredentials: [],
authenticatorSelection: {
residentKey: "preferred",
userVerification: "preferred",
requireResidentKey: false
},
extensions: { credProps: true }
} And @simplewebauthn/server is still a CommonJS module in this test, hence my surprise. My ultimate goal is to output two builds, one CommonJS and one ESM, but I'd say I can now advertise "Deno support" 馃殌 Edit: I just realized that, now, I can advertise "unofficial" Deno support because you have to use the |
Don't take it as a request to do it here and now. But Deno support would be nice. Thanks for your qualitative webauthn JS helpers and docs 馃憤
The text was updated successfully, but these errors were encountered: