mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 11:04:25 +00:00
Merge branch 'main' into rc
This commit is contained in:
commit
8d05a0d6d8
@ -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;
|
||||
}
|
63
internal/api/ui/login/static/resources/scripts/utils.js
Normal file
63
internal/api/ui/login/static/resources/scripts/utils.js
Normal 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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user