feat: initial commit
This commit is contained in:
3
public/css/style.css
Normal file
3
public/css/style.css
Normal file
@ -0,0 +1,3 @@
|
||||
body {
|
||||
background-color: hsla(217, 15%, 95%, 1);
|
||||
}
|
91
public/js/hydra-webauthn.js
Normal file
91
public/js/hydra-webauthn.js
Normal file
@ -0,0 +1,91 @@
|
||||
(function(HydraWebAuthn) {
|
||||
|
||||
HydraWebAuthn.base64ToArrayBuffer = function(base64) {
|
||||
var binaryString = HydraWebAuthn.fromBase64URL(base64);
|
||||
var bytes = new Uint8Array(binaryString.length);
|
||||
for (var i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
HydraWebAuthn.arrayBufferToBase64 = function( buffer ) {
|
||||
var binary = '';
|
||||
var bytes = new Uint8Array( buffer );
|
||||
var len = bytes.byteLength;
|
||||
for (var i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode( bytes[ i ] );
|
||||
}
|
||||
return window.btoa( binary );
|
||||
}
|
||||
|
||||
HydraWebAuthn.toJSONCredentials = function(credentials) {
|
||||
return {
|
||||
id: credentials.id,
|
||||
rawId: HydraWebAuthn.toBase64URL(credentials.rawId),
|
||||
authenticatorAttachment: credentials.authenticatorAttachment,
|
||||
type: "public-key",
|
||||
clientExtensionResults: (() => {
|
||||
const results = credentials.getClientExtensionResults();
|
||||
return Object.keys(results).reduce((json, key) => {
|
||||
json[key] = HydraWebAuthn.urlsafe(HydraWebAuthn.arrayBufferToBase64(results[key]))
|
||||
return json;
|
||||
}, {})
|
||||
})(),
|
||||
response: {
|
||||
attestationObject: HydraWebAuthn.urlsafe(HydraWebAuthn.arrayBufferToBase64(credentials.response.attestationObject)),
|
||||
clientDataJSON: HydraWebAuthn.urlsafe(HydraWebAuthn.arrayBufferToBase64(credentials.response.clientDataJSON))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HydraWebAuthn.toJSONAssertion = function(assertion) {
|
||||
return {
|
||||
id: assertion.id,
|
||||
rawId: HydraWebAuthn.urlsafe(HydraWebAuthn.arrayBufferToBase64(assertion.rawId)),
|
||||
type: "public-key",
|
||||
response: {
|
||||
authenticatorData: HydraWebAuthn.urlsafe(HydraWebAuthn.arrayBufferToBase64(assertion.response.authenticatorData)),
|
||||
clientDataJSON: HydraWebAuthn.urlsafe(HydraWebAuthn.arrayBufferToBase64(assertion.response.clientDataJSON)),
|
||||
signature: HydraWebAuthn.urlsafe(HydraWebAuthn.arrayBufferToBase64(assertion.response.signature)),
|
||||
userHandle: assertion.response.userHandle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HydraWebAuthn.base64 = function(str) {
|
||||
return HydraWebAuthn.bytesToBase64(new TextEncoder().encode(str));
|
||||
}
|
||||
|
||||
HydraWebAuthn.toBase64URL = function(str) {
|
||||
return HydraWebAuthn.urlsafe(HydraWebAuthn.base64(str));
|
||||
}
|
||||
|
||||
HydraWebAuthn.fromBase64URL = function(str) {
|
||||
str = str
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
|
||||
var pad = str.length % 4;
|
||||
if(pad) {
|
||||
if(pad === 1) {
|
||||
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
|
||||
}
|
||||
str += new Array(5-pad).join('=');
|
||||
}
|
||||
|
||||
return atob(str);
|
||||
}
|
||||
|
||||
HydraWebAuthn.urlsafe = function(base64) {
|
||||
return base64.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=+$/, '')
|
||||
}
|
||||
|
||||
HydraWebAuthn.bytesToBase64 = function(bytes) {
|
||||
const binString = String.fromCodePoint(...bytes);
|
||||
return btoa(binString);
|
||||
}
|
||||
|
||||
}(window.HydraWebAuthn = {}))
|
20
public/js/login.js
Normal file
20
public/js/login.js
Normal file
@ -0,0 +1,20 @@
|
||||
const rawAssertionRequest = HydraWebAuthn.fromBase64URL(document.currentScript.dataset.assertionRequest);
|
||||
const assertionRequest = JSON.parse(rawAssertionRequest);
|
||||
assertionRequest.publicKey.challenge = HydraWebAuthn.base64ToArrayBuffer(assertionRequest.publicKey.challenge)
|
||||
assertionRequest.publicKey.allowCredentials.forEach(credential => credential.id = HydraWebAuthn.base64ToArrayBuffer(credential.id))
|
||||
|
||||
console.log("Assertion request", assertionRequest)
|
||||
|
||||
navigator.credentials.get(assertionRequest)
|
||||
.then(assertion => {
|
||||
console.log("Assertion", assertion)
|
||||
if (assertion) {
|
||||
const jsonAssertion = JSON.stringify(HydraWebAuthn.toJSONAssertion(assertion), null, 2);
|
||||
console.log(jsonAssertion);
|
||||
document.getElementById("assertion").value = HydraWebAuthn.toBase64URL(jsonAssertion);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
}).finally(() => {
|
||||
document.getElementById("login").submit();
|
||||
})
|
17
public/js/register.js
Normal file
17
public/js/register.js
Normal file
@ -0,0 +1,17 @@
|
||||
const rawWebAuthnOptions = HydraWebAuthn.fromBase64URL(document.currentScript.dataset.webAuthnOptions);
|
||||
const webAuthnOptions = JSON.parse(rawWebAuthnOptions);
|
||||
|
||||
webAuthnOptions.publicKey.challenge = HydraWebAuthn.base64ToArrayBuffer(webAuthnOptions.publicKey.challenge);
|
||||
webAuthnOptions.publicKey.user.id = HydraWebAuthn.base64ToArrayBuffer(webAuthnOptions.publicKey.user.id);
|
||||
|
||||
console.log(webAuthnOptions)
|
||||
|
||||
function generateCredentials() {
|
||||
navigator.credentials.create(webAuthnOptions).then(credentials => {
|
||||
if (!credentials) return;
|
||||
const jsonCredentials = JSON.stringify(HydraWebAuthn.toJSONCredentials(credentials), null, 2);
|
||||
console.log(jsonCredentials);
|
||||
document.getElementById("credentials").value = HydraWebAuthn.toBase64URL(jsonCredentials);
|
||||
document.getElementById("register").submit();
|
||||
}).catch(err => console.error(err));
|
||||
}
|
Reference in New Issue
Block a user