org.springframework.security.spring-security-webauthn.js Maven / Gradle / Ivy
The newest version!
"use strict";
(() => {
// lib/base64url.js
var base64url_default = {
encode: function(buffer) {
const base64 = window.btoa(String.fromCharCode(...new Uint8Array(buffer)));
return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
},
decode: function(base64url) {
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
const binStr = window.atob(base64);
const bin = new Uint8Array(binStr.length);
for (let i = 0; i < binStr.length; i++) {
bin[i] = binStr.charCodeAt(i);
}
return bin.buffer;
}
};
// lib/http.js
async function post(url, headers, body) {
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
...headers
}
};
if (body) {
options.body = JSON.stringify(body);
}
return fetch(url, options);
}
var http_default = { post };
// lib/abort-controller.js
var holder = {
controller: new AbortController()
};
function newSignal() {
if (!!holder.controller) {
holder.controller.abort("Initiating new WebAuthN ceremony, cancelling current ceremony");
}
holder.controller = new AbortController();
return holder.controller.signal;
}
var abort_controller_default = {
newSignal
};
// lib/webauthn-core.js
async function isConditionalMediationAvailable() {
return !!(window.PublicKeyCredential && window.PublicKeyCredential.isConditionalMediationAvailable && await window.PublicKeyCredential.isConditionalMediationAvailable());
}
async function authenticate(headers, contextPath, useConditionalMediation) {
let options;
try {
const optionsResponse = await http_default.post(`${contextPath}/webauthn/authenticate/options`, headers);
if (!optionsResponse.ok) {
throw new Error(`HTTP ${optionsResponse.status}`);
}
options = await optionsResponse.json();
} catch (err) {
throw new Error(`Authentication failed. Could not fetch authentication options: ${err.message}`, { cause: err });
}
const decodedOptions = {
...options,
challenge: base64url_default.decode(options.challenge)
};
const credentialOptions = {
publicKey: decodedOptions,
signal: abort_controller_default.newSignal()
};
if (useConditionalMediation) {
credentialOptions.mediation = "conditional";
}
let cred;
try {
cred = await navigator.credentials.get(credentialOptions);
} catch (err) {
throw new Error(`Authentication failed. Call to navigator.credentials.get failed: ${err.message}`, { cause: err });
}
const { response, type: credType } = cred;
let userHandle;
if (response.userHandle) {
userHandle = base64url_default.encode(response.userHandle);
}
const body = {
id: cred.id,
rawId: base64url_default.encode(cred.rawId),
response: {
authenticatorData: base64url_default.encode(response.authenticatorData),
clientDataJSON: base64url_default.encode(response.clientDataJSON),
signature: base64url_default.encode(response.signature),
userHandle
},
credType,
clientExtensionResults: cred.getClientExtensionResults(),
authenticatorAttachment: cred.authenticatorAttachment
};
let authenticationResponse;
try {
const authenticationCallResponse = await http_default.post(`${contextPath}/login/webauthn`, headers, body);
if (!authenticationCallResponse.ok) {
throw new Error(`HTTP ${authenticationCallResponse.status}`);
}
authenticationResponse = await authenticationCallResponse.json();
} catch (err) {
throw new Error(`Authentication failed. Could not process the authentication request: ${err.message}`, {
cause: err
});
}
if (!(authenticationResponse && authenticationResponse.authenticated && authenticationResponse.redirectUrl)) {
throw new Error(
`Authentication failed. Expected {"authenticated": true, "redirectUrl": "..."}, server responded with: ${JSON.stringify(authenticationResponse)}`
);
}
return authenticationResponse.redirectUrl;
}
async function register(headers, contextPath, label) {
if (!label) {
throw new Error("Error: Passkey Label is required");
}
let options;
try {
const optionsResponse = await http_default.post(`${contextPath}/webauthn/register/options`, headers);
if (!optionsResponse.ok) {
throw new Error(`Server responded with HTTP ${optionsResponse.status}`);
}
options = await optionsResponse.json();
} catch (e) {
throw new Error(`Registration failed. Could not fetch registration options: ${e.message}`, { cause: e });
}
const decodedExcludeCredentials = !options.excludeCredentials ? [] : options.excludeCredentials.map((cred) => ({
...cred,
id: base64url_default.decode(cred.id)
}));
const decodedOptions = {
...options,
user: {
...options.user,
id: base64url_default.decode(options.user.id)
},
challenge: base64url_default.decode(options.challenge),
excludeCredentials: decodedExcludeCredentials
};
let credentialsContainer;
try {
credentialsContainer = await navigator.credentials.create({
publicKey: decodedOptions,
signal: abort_controller_default.newSignal()
});
} catch (e) {
throw new Error(`Registration failed. Call to navigator.credentials.create failed: ${e.message}`, { cause: e });
}
const { response } = credentialsContainer;
const credential = {
id: credentialsContainer.id,
rawId: base64url_default.encode(credentialsContainer.rawId),
response: {
attestationObject: base64url_default.encode(response.attestationObject),
clientDataJSON: base64url_default.encode(response.clientDataJSON),
transports: response.getTransports ? response.getTransports() : []
},
type: credentialsContainer.type,
clientExtensionResults: credentialsContainer.getClientExtensionResults(),
authenticatorAttachment: credentialsContainer.authenticatorAttachment
};
const registrationRequest = {
publicKey: {
credential,
label
}
};
let verificationJSON;
try {
const verificationResp = await http_default.post(`${contextPath}/webauthn/register`, headers, registrationRequest);
if (!verificationResp.ok) {
throw new Error(`HTTP ${verificationResp.status}`);
}
verificationJSON = await verificationResp.json();
} catch (e) {
throw new Error(`Registration failed. Could not process the registration request: ${e.message}`, { cause: e });
}
if (!(verificationJSON && verificationJSON.success)) {
throw new Error(`Registration failed. Server responded with: ${JSON.stringify(verificationJSON)}`);
}
}
var webauthn_core_default = {
authenticate,
register,
isConditionalMediationAvailable
};
// lib/webauthn-login.js
async function authenticateOrError(headers, contextPath, useConditionalMediation) {
try {
const redirectUrl = await webauthn_core_default.authenticate(headers, contextPath, useConditionalMediation);
window.location.href = redirectUrl;
} catch (err) {
console.error(err);
window.location.href = `${contextPath}/login?error`;
}
}
async function setupLogin(headers, contextPath, signinButton) {
signinButton.addEventListener("click", async () => {
await authenticateOrError(headers, contextPath, false);
});
}
// lib/webauthn-registration.js
function setVisibility(element, value) {
if (!element) {
return;
}
element.style.display = value ? "block" : "none";
}
function setError(ui, msg) {
resetPopups(ui);
const error = ui.getError();
if (!error) {
return;
}
error.textContent = msg;
setVisibility(error, true);
}
function setSuccess(ui) {
resetPopups(ui);
const success = ui.getSuccess();
if (!success) {
return;
}
setVisibility(success, true);
}
function resetPopups(ui) {
const success = ui.getSuccess();
const error = ui.getError();
setVisibility(success, false);
setVisibility(error, false);
}
async function submitDeleteForm(contextPath, form, headers) {
const options = {
method: "DELETE",
headers: {
"Content-Type": "application/json",
...headers
}
};
await fetch(form.action, options);
}
async function setupRegistration(headers, contextPath, ui) {
resetPopups(ui);
if (!window.PublicKeyCredential) {
setError(ui, "WebAuthn is not supported");
return;
}
const queryString = new URLSearchParams(window.location.search);
if (queryString.has("success")) {
setSuccess(ui);
}
ui.getRegisterButton().addEventListener("click", async () => {
resetPopups(ui);
const label = ui.getLabelInput().value;
try {
await webauthn_core_default.register(headers, contextPath, label);
window.location.href = `${contextPath}/webauthn/register?success`;
} catch (err) {
setError(ui, err.message);
console.error(err);
}
});
ui.getDeleteForms().forEach(
(form) => form.addEventListener("submit", async function(e) {
e.preventDefault();
try {
await submitDeleteForm(contextPath, form, headers);
window.location.href = `${contextPath}/webauthn/register?success`;
} catch (err) {
setError(ui, err.message);
}
})
);
}
// lib/index.js
window.setupLogin = setupLogin;
window.setupRegistration = setupRegistration;
})();
© 2015 - 2024 Weber Informatics LLC | Privacy Policy