Merge branch 'main' into rc

This commit is contained in:
adlerhurst 2023-06-08 09:30:12 +02:00
commit 8d05a0d6d8
11 changed files with 186 additions and 162 deletions

View File

@ -1,68 +0,0 @@
/*
* modified version of:
*
* base64-arraybuffer
* https://github.com/niklasvh/base64-arraybuffer
*
* Copyright (c) 2012 Niklas von Hertzen
* Licensed under the MIT license.
*/
"use strict";
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Use a lookup table to find the index.
let lookup = new Uint8Array(256);
for (var i = 0; i < chars.length; i++) {
lookup[chars.charCodeAt(i)] = i;
}
function encode(arraybuffer) {
let bytes = new Uint8Array(arraybuffer),
i, len = bytes.length, base64 = "";
for (i = 0; i < len; i += 3) {
base64 += chars[bytes[i] >> 2];
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
base64 += chars[bytes[i + 2] & 63];
}
if ((len % 3) === 2) {
base64 = base64.substring(0, base64.length - 1) + "=";
} else if (len % 3 === 1) {
base64 = base64.substring(0, base64.length - 2) + "==";
}
return base64;
}
function decode(base64) {
let bufferLength = base64.length * 0.75,
len = base64.length, i, p = 0,
encoded1, encoded2, encoded3, encoded4;
if (base64[base64.length - 1] === "=") {
bufferLength--;
if (base64[base64.length - 2] === "=") {
bufferLength--;
}
}
let arraybuffer = new ArrayBuffer(bufferLength),
bytes = new Uint8Array(arraybuffer);
for (i = 0; i < len; i += 4) {
encoded1 = lookup[base64.charCodeAt(i)];
encoded2 = lookup[base64.charCodeAt(i + 1)];
encoded3 = lookup[base64.charCodeAt(i + 2)];
encoded4 = lookup[base64.charCodeAt(i + 3)];
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
}
return arraybuffer;
}

View File

@ -0,0 +1,63 @@
function coerceToBase64Url(thing, name) {
// Array or ArrayBuffer to Uint8Array
if (Array.isArray(thing)) {
thing = Uint8Array.from(thing);
}
if (thing instanceof ArrayBuffer) {
thing = new Uint8Array(thing);
}
// Uint8Array to base64
if (thing instanceof Uint8Array) {
var str = "";
var len = thing.byteLength;
for (var i = 0; i < len; i++) {
str += String.fromCharCode(thing[i]);
}
thing = window.btoa(str);
}
if (typeof thing !== "string") {
throw new Error("could not coerce '" + name + "' to string");
}
// base64 to base64url
// NOTE: "=" at the end of challenge is optional, strip it off here
thing = thing.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, "");
return thing;
}
function coerceToArrayBuffer(thing, name) {
if (typeof thing === "string") {
// base64url to base64
thing = thing.replace(/-/g, "+").replace(/_/g, "/");
// base64 to Uint8Array
var str = window.atob(thing);
var bytes = new Uint8Array(str.length);
for (var i = 0; i < str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
thing = bytes;
}
// Array to Uint8Array
if (Array.isArray(thing)) {
thing = new Uint8Array(thing);
}
// Uint8Array to ArrayBuffer
if (thing instanceof Uint8Array) {
thing = thing.buffer;
}
// error if none of the above worked
if (!(thing instanceof ArrayBuffer)) {
throw new TypeError("could not coerce '" + name + "' to ArrayBuffer");
}
return thing;
}

View File

@ -1,31 +1,28 @@
function checkWebauthnSupported(button, func) {
let support = document.getElementsByClassName("wa-support");
let noSupport = document.getElementsByClassName("wa-no-support");
if (!window.PublicKeyCredential) {
for (let item of noSupport) {
item.classList.remove('hidden');
}
for (let item of support) {
item.classList.add('hidden');
}
return;
let support = document.getElementsByClassName("wa-support");
let noSupport = document.getElementsByClassName("wa-no-support");
if (!window.PublicKeyCredential) {
for (let item of noSupport) {
item.classList.remove("hidden");
}
document.getElementById(button).addEventListener('click', func);
for (let item of support) {
item.classList.add("hidden");
}
return;
}
document.getElementById(button).addEventListener("click", func);
}
function webauthnError(error) {
let err = document.getElementById('wa-error');
err.getElementsByClassName('cause')[0].innerText = error.message;
err.classList.remove('hidden');
let err = document.getElementById("wa-error");
err.getElementsByClassName("cause")[0].innerText = error.message;
err.classList.remove("hidden");
}
function bufferDecode(value) {
return decode(value);
function bufferDecode(value, name) {
return coerceToArrayBuffer(value, name);
}
function bufferEncode(value) {
return encode(value)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
function bufferEncode(value, name) {
return coerceToBase64Url(value, name);
}

View File

@ -1,41 +1,54 @@
document.addEventListener('DOMContentLoaded', checkWebauthnSupported('btn-login', login));
document.addEventListener(
"DOMContentLoaded",
checkWebauthnSupported("btn-login", login)
);
function login() {
document.getElementById('wa-error').classList.add('hidden');
document.getElementById("wa-error").classList.add("hidden");
let makeAssertionOptions = JSON.parse(atob(document.getElementsByName('credentialAssertionData')[0].value));
makeAssertionOptions.publicKey.challenge = bufferDecode(makeAssertionOptions.publicKey.challenge);
makeAssertionOptions.publicKey.allowCredentials.forEach(function (listItem) {
listItem.id = bufferDecode(listItem.id)
});
navigator.credentials.get({
publicKey: makeAssertionOptions.publicKey
}).then(function (credential) {
verifyAssertion(credential);
}).catch(function (err) {
webauthnError(err);
let makeAssertionOptions = JSON.parse(
atob(document.getElementsByName("credentialAssertionData")[0].value)
);
makeAssertionOptions.publicKey.challenge = bufferDecode(
makeAssertionOptions.publicKey.challenge,
"publicKey.challenge"
);
makeAssertionOptions.publicKey.allowCredentials.forEach(function (listItem) {
listItem.id = bufferDecode(listItem.id, "publicKey.allowCredentials.id");
});
navigator.credentials
.get({
publicKey: makeAssertionOptions.publicKey,
})
.then(function (credential) {
verifyAssertion(credential);
})
.catch(function (err) {
webauthnError(err);
});
}
function verifyAssertion(assertedCredential) {
let authData = new Uint8Array(assertedCredential.response.authenticatorData);
let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON);
let rawId = new Uint8Array(assertedCredential.rawId);
let sig = new Uint8Array(assertedCredential.response.signature);
let userHandle = new Uint8Array(assertedCredential.response.userHandle);
let authData = new Uint8Array(assertedCredential.response.authenticatorData);
let clientDataJSON = new Uint8Array(
assertedCredential.response.clientDataJSON
);
let rawId = new Uint8Array(assertedCredential.rawId);
let sig = new Uint8Array(assertedCredential.response.signature);
let userHandle = new Uint8Array(assertedCredential.response.userHandle);
let data = JSON.stringify({
id: assertedCredential.id,
rawId: bufferEncode(rawId),
type: assertedCredential.type,
response: {
authenticatorData: bufferEncode(authData),
clientDataJSON: bufferEncode(clientDataJSON),
signature: bufferEncode(sig),
userHandle: bufferEncode(userHandle),
},
})
let data = JSON.stringify({
id: assertedCredential.id,
rawId: bufferEncode(rawId),
type: assertedCredential.type,
response: {
authenticatorData: bufferEncode(authData),
clientDataJSON: bufferEncode(clientDataJSON),
signature: bufferEncode(sig),
userHandle: bufferEncode(userHandle),
},
});
document.getElementsByName('credentialData')[0].value = btoa(data);
document.getElementsByTagName('form')[0].submit();
}
document.getElementsByName("credentialData")[0].value = btoa(data);
document.getElementsByTagName("form")[0].submit();
}

View File

@ -1,42 +1,61 @@
document.addEventListener('DOMContentLoaded', checkWebauthnSupported('btn-register', registerCredential));
document.addEventListener(
"DOMContentLoaded",
checkWebauthnSupported("btn-register", registerCredential)
);
function registerCredential() {
document.getElementById('wa-error').classList.add('hidden');
document.getElementById("wa-error").classList.add("hidden");
let opt = JSON.parse(atob(document.getElementsByName('credentialCreationData')[0].value));
opt.publicKey.challenge = bufferDecode(opt.publicKey.challenge);
opt.publicKey.user.id = bufferDecode(opt.publicKey.user.id);
if (opt.publicKey.excludeCredentials) {
for (let i = 0; i < opt.publicKey.excludeCredentials.length; i++) {
if (opt.publicKey.excludeCredentials[i].id !== null) {
opt.publicKey.excludeCredentials[i].id = bufferDecode(opt.publicKey.excludeCredentials[i].id);
}
}
let opt = JSON.parse(
atob(document.getElementsByName("credentialCreationData")[0].value)
);
opt.publicKey.challenge = bufferDecode(
opt.publicKey.challenge,
"publicKey.challenge"
);
opt.publicKey.user.id = bufferDecode(
opt.publicKey.user.id,
"publicKey.user.id"
);
if (opt.publicKey.excludeCredentials) {
for (let i = 0; i < opt.publicKey.excludeCredentials.length; i++) {
if (opt.publicKey.excludeCredentials[i].id !== null) {
opt.publicKey.excludeCredentials[i].id = bufferDecode(
opt.publicKey.excludeCredentials[i].id,
"publicKey.excludeCredentials"
);
}
}
navigator.credentials.create({
publicKey: opt.publicKey
}).then(function (credential) {
createCredential(credential);
}).catch(function (err) {
webauthnError(err);
}
navigator.credentials
.create({
publicKey: opt.publicKey,
})
.then(function (credential) {
createCredential(credential);
})
.catch(function (err) {
webauthnError(err);
});
}
function createCredential(newCredential) {
let attestationObject = new Uint8Array(newCredential.response.attestationObject);
let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON);
let rawId = new Uint8Array(newCredential.rawId);
let attestationObject = new Uint8Array(
newCredential.response.attestationObject
);
let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON);
let rawId = new Uint8Array(newCredential.rawId);
let data = JSON.stringify({
id: newCredential.id,
rawId: bufferEncode(rawId),
type: newCredential.type,
response: {
attestationObject: bufferEncode(attestationObject),
clientDataJSON: bufferEncode(clientDataJSON),
},
});
let data = JSON.stringify({
id: newCredential.id,
rawId: bufferEncode(rawId),
type: newCredential.type,
response: {
attestationObject: bufferEncode(attestationObject),
clientDataJSON: bufferEncode(clientDataJSON),
},
});
document.getElementsByName('credentialData')[0].value = btoa(data);
document.getElementsByTagName('form')[0].submit();
}
document.getElementsByName("credentialData")[0].value = btoa(data);
document.getElementsByTagName("form")[0].submit();
}

View File

@ -218,7 +218,7 @@ body.waiting * {
footer {
width: 100%;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.1254901961);
background: #00000020;
min-height: 50px;
display: flex;
align-items: center;
@ -759,7 +759,7 @@ i {
letter-spacing: 0.05em;
font-size: 12px;
white-space: nowrap;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1019607843);
box-shadow: 0 0 3px #0000001a;
width: fit-content;
line-height: 1rem;
}
@ -1211,7 +1211,7 @@ i {
footer {
width: 100%;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.1254901961);
background: #00000020;
min-height: 50px;
display: flex;
align-items: center;
@ -1752,7 +1752,7 @@ i {
letter-spacing: 0.05em;
font-size: 12px;
white-space: nowrap;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1019607843);
box-shadow: 0 0 3px #0000001a;
width: fit-content;
line-height: 1rem;
}

File diff suppressed because one or more lines are too long

View File

@ -41,7 +41,7 @@
</div>
</form>
<script src="{{ resourceUrl "scripts/base64.js" }}"></script>
<script src="{{ resourceUrl "scripts/utils.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn_register.js" }}"></script>

View File

@ -41,7 +41,7 @@
{{ end }}
</form>
<script src="{{ resourceUrl "scripts/base64.js" }}"></script>
<script src="{{ resourceUrl "scripts/utils.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn_login.js" }}"></script>

View File

@ -37,7 +37,7 @@
</div>
</form>
<script src="{{ resourceUrl "scripts/base64.js" }}"></script>
<script src="{{ resourceUrl "scripts/utils.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn_login.js" }}"></script>

View File

@ -45,7 +45,7 @@
</div>
</form>
<script src="{{ resourceUrl "scripts/base64.js" }}"></script>
<script src="{{ resourceUrl "scripts/utils.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn.js" }}"></script>
<script src="{{ resourceUrl "scripts/webauthn_register.js" }}"></script>