-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add naive methods
create
and get
- Loading branch information
Showing
7 changed files
with
188 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"tabWidth": 2, | ||
"printWidth": 100 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
)} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |