Skip to content

Commit

Permalink
feat: add naive methods create and get
Browse files Browse the repository at this point in the history
  • Loading branch information
yum0e committed Oct 26, 2023
1 parent d194d6a commit e60e3ed
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 3 deletions.
4 changes: 4 additions & 0 deletions front/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"tabWidth": 2,
"printWidth": 100
}
6 changes: 3 additions & 3 deletions front/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
module.exports = {
reactStrictMode: true,
webpack: (config) => {
config.resolve.fallback = { fs: false, net: false, tls: false }
return config
config.resolve.fallback = { fs: false, net: false, tls: false };
return config;
},
}
};
3 changes: 3 additions & 0 deletions front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"@emotion/styled": "^11.11.0",
"@radix-ui/themes": "^2.0.0",
"@rainbow-me/rainbowkit": "^1.0.0",
"@simplewebauthn/browser": "^8.3.3",
"@simplewebauthn/typescript-types": "^8.3.3",
"@walletconnect/core": "^2.10.4",
"next": "^13.4.0",
"react": "^18.2.0",
Expand All @@ -26,6 +28,7 @@
"@types/react-dom": "^18.0.3",
"eslint": "^8.15.0",
"eslint-config-next": "^12.1.6",
"prettier": "^3.0.3",
"typescript": "^5.0.4"
}
}
25 changes: 25 additions & 0 deletions front/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions front/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BlockNumber } from "../components/BlockNumber";
import { ConnectButton } from "../components/ConnectButton";
import { Connected } from "../components/Connected";
import { NetworkSwitcher } from "../components/NetworkSwitcher";
import PassKey from '../components/PassKey'
import { ReadContract } from "../components/ReadContract";
import { ReadContracts } from "../components/ReadContracts";
import { ReadContractsInfinite } from "../components/ReadContractsInfinite";
Expand All @@ -25,6 +26,7 @@ export function Page() {
<h1>wagmi + RainbowKit + Next.js</h1>

<ConnectButton />
<PassKey />

<Connected>
<hr />
Expand Down
64 changes: 64 additions & 0 deletions front/src/components/PassKey.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use client";

import { useEffect, useState } from "react";
import WebAuthn from "../libs/webauthn";

const webauthn = new WebAuthn();

function arrayBufferToString(buffer: ArrayBuffer) {
let str = "";
const array = new Uint8Array(buffer);
for (let i = 0; i < array.length; i++) {
str += String.fromCharCode(array[i]);
}
return str;
}

export default function PassKey() {
const [username, setUsername] = useState<string>("super-user");
const [credential, setCredential] = useState<Credential | null>(null);
const [clientDataJson, setClientDataJson] = useState<string | null>(null);

function onUsernameChange(event: React.ChangeEvent<HTMLInputElement>) {
setUsername(event.target.value);
}

async function onCreate() {
setCredential(await webauthn.create({ username }));
}

async function onGet() {
setCredential(await webauthn.get());
}

useEffect(() => {
let cred = credential as unknown as { response: { clientDataJSON: ArrayBuffer } };
console.log("cred", cred);
let clientDataJson = arrayBufferToString(cred?.response?.clientDataJSON);
setClientDataJson(clientDataJson);
let challenge = JSON.parse(JSON.stringify(clientDataJson)) as { challenge: string };
console.log("client data json", clientDataJson);
console.log("challenge", challenge?.challenge);
}, [credential, clientDataJson]);

return (
<>
<div style={{ content: "center", margin: 20 }}>
<input
type="outlined"
name="username"
autoComplete="webauthn"
value={username}
onChange={onUsernameChange}
/>
<button onClick={onCreate}>Create</button>
<button onClick={onGet}>Get</button>
</div>
{credential && (
<div style={{ content: "center", margin: 20 }}>
ClientDataJSON: {JSON.stringify(clientDataJson, null, 2)}
</div>
)}
</>
);
}
87 changes: 87 additions & 0 deletions front/src/libs/webauthn/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import crypto from "crypto";

export default class WebAuthn {
private _generateRandomBytes(): Buffer {
return crypto.randomBytes(16);
}

async create({ username }: { username: string }): Promise<Credential | null> {
const options: PublicKeyCredentialCreationOptions = {
timeout: 60000,
rp: {
name: "hocuspocusxyz",
id: "localhost",
},
user: {
id: this._generateRandomBytes(),
name: username,
displayName: username,
},
pubKeyCredParams: [
{ alg: -8, type: "public-key" }, // Ed25519
{ alg: -7, type: "public-key" }, // ES256
{ alg: -257, type: "public-key" }, // RS256
],
authenticatorSelection: {
// authenticatorAttachment: "cross-platform",
requireResidentKey: true,
userVerification: "required",
},
// extensions: {
// prf: {
// eval: {
// first: new TextEncoder().encode("Foo encryption key").buffer,
// },
// },
// } as AuthenticationExtensionsClientInputs,
// Challenge is unused during registration, but must be present
challenge: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]).buffer,
};

const credential = await navigator.credentials.create({
publicKey: options,
});

console.log("CREATE CREDENTIAL: ", credential);
return credential;
}

async get(): Promise<Credential | null> {
const options: PublicKeyCredentialRequestOptions = {
timeout: 60000,
challenge: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]).buffer,
rpId: "localhost",
userVerification: "required",
extensions: {
prf: {
// hmacCreateSecret: true,
eval: {
first: new TextEncoder().encode("Foo encryption key").buffer,
},
},
} as AuthenticationExtensionsClientInputs,
};

const credential = await navigator.credentials.get({
publicKey: options,
});

// const result = btoa(
// String.fromCharCode.apply(
// null,
// new Uint8Array(
// credential.getClientExtensionResults().prf.results.first
// ) as any
// )
// );

// console.log(
// "CREDENTIAL: ",
// new TextDecoder("utf-8").decode(
// new Uint8Array((credential as any).response.clientDataJSON)
// )
// );
console.log("GET CREDENTIAL: ", credential);
return credential;
}
}

0 comments on commit e60e3ed

Please sign in to comment.