mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Fix passkey auth flow redirects (#3123)
* Fix passkey auth flow redirects * Handle webauthn error
This commit is contained in:
parent
c9bf304bc0
commit
5064712fe6
|
@ -1,4 +1,5 @@
|
|||
use super::{cookies, empty_string_as_none, HtmlTemplate, UnrecoverableErrorView};
|
||||
use crate::https::views::errors::HtmxError;
|
||||
use crate::https::{
|
||||
extractors::{DomainInfo, DomainInfoRead, VerifiedClientInformation},
|
||||
middleware::KOpId,
|
||||
|
@ -489,16 +490,30 @@ pub async fn view_login_backupcode_post(
|
|||
credential_step(state, kopid, jar, client_auth_info, auth_cred, domain_info).await
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct JsonedPublicKeyCredential {
|
||||
cred: String,
|
||||
}
|
||||
|
||||
pub async fn view_login_passkey_post(
|
||||
State(state): State<ServerState>,
|
||||
Extension(kopid): Extension<KOpId>,
|
||||
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
Json(assertion): Json<Box<PublicKeyCredential>>,
|
||||
Form(assertion): Form<JsonedPublicKeyCredential>,
|
||||
) -> Response {
|
||||
let auth_cred = AuthCredential::Passkey(assertion);
|
||||
credential_step(state, kopid, jar, client_auth_info, auth_cred, domain_info).await
|
||||
let result = serde_json::from_str::<Box<PublicKeyCredential>>(assertion.cred.as_str());
|
||||
match result {
|
||||
Ok(pkc) => {
|
||||
let auth_cred = AuthCredential::Passkey(pkc);
|
||||
credential_step(state, kopid, jar, client_auth_info, auth_cred, domain_info).await
|
||||
}
|
||||
Err(e) => {
|
||||
error!(err = ?e, "Unable to deserialize credential submission");
|
||||
HtmxError::new(&kopid, OperationError::SerdeJsonError).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn view_login_seckey_post(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
function asskey_login(target) {
|
||||
function asskey_login() {
|
||||
let credentialRequestOptions = JSON.parse(document.getElementById('data').textContent);
|
||||
credentialRequestOptions.publicKey.challenge = Base64.toUint8Array(credentialRequestOptions.publicKey.challenge);
|
||||
credentialRequestOptions.publicKey.allowCredentials?.forEach(function (listItem) {
|
||||
|
@ -8,46 +8,35 @@ function asskey_login(target) {
|
|||
|
||||
navigator.credentials.get({ publicKey: credentialRequestOptions.publicKey })
|
||||
.then((assertion) => {
|
||||
const myRequest = new Request(target, {
|
||||
method: 'POST',
|
||||
redirect: 'follow',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
document.getElementById("cred").value = JSON.stringify({
|
||||
id: assertion.id,
|
||||
rawId: Base64.fromUint8Array(new Uint8Array(assertion.rawId), true),
|
||||
type: assertion.type,
|
||||
response: {
|
||||
authenticatorData: Base64.fromUint8Array(new Uint8Array(assertion.response.authenticatorData), true),
|
||||
clientDataJSON: Base64.fromUint8Array(new Uint8Array(assertion.response.clientDataJSON), true),
|
||||
signature: Base64.fromUint8Array(new Uint8Array(assertion.response.signature), true),
|
||||
userHandle: Base64.fromUint8Array(new Uint8Array(assertion.response.userHandle), true)
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: assertion.id,
|
||||
rawId: Base64.fromUint8Array(new Uint8Array(assertion.rawId), true),
|
||||
type: assertion.type,
|
||||
response: {
|
||||
authenticatorData: Base64.fromUint8Array(new Uint8Array(assertion.response.authenticatorData), true),
|
||||
clientDataJSON: Base64.fromUint8Array(new Uint8Array(assertion.response.clientDataJSON), true),
|
||||
signature: Base64.fromUint8Array(new Uint8Array(assertion.response.signature), true),
|
||||
userHandle: Base64.fromUint8Array(new Uint8Array(assertion.response.userHandle), true)
|
||||
},
|
||||
}),
|
||||
});
|
||||
fetch(myRequest).then((response) => {
|
||||
if (response.redirected) {
|
||||
window.location.replace(response.url);
|
||||
return;
|
||||
} else {
|
||||
console.error("expected a redirect");
|
||||
}
|
||||
})
|
||||
})
|
||||
document.getElementById("cred-form").submit();
|
||||
}).catch((error) => {
|
||||
console.error(`Failed to complete passkey authentication: ${error}`);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const myButton = document.getElementById("start-passkey-button");
|
||||
myButton.addEventListener("click", () => {
|
||||
asskey_login('/ui/login/passkey');
|
||||
asskey_login();
|
||||
});
|
||||
} catch (_error) {};
|
||||
|
||||
try {
|
||||
const myButton = document.getElementById("start-seckey-button");
|
||||
myButton.addEventListener("click", () => {
|
||||
asskey_login('/ui/login/seckey');
|
||||
asskey_login();
|
||||
});
|
||||
} catch (_error) {};
|
||||
|
||||
|
|
|
@ -13,11 +13,19 @@
|
|||
defer></script>
|
||||
|
||||
(% if passkey %)
|
||||
<button hx-disable type="button" class="btn btn-dark"
|
||||
id="start-passkey-button">Use Passkey</button>
|
||||
<form id="cred-form" action="/ui/login/passkey" method="POST">
|
||||
<input hidden="hidden" name="cred" id="cred">
|
||||
|
||||
<button hx-disable type="button" class="btn btn-dark"
|
||||
id="start-passkey-button">Use Passkey</button>
|
||||
</form>
|
||||
(% else %)
|
||||
<button type="button" class="btn btn-dark" id="start-seckey-button">Use Security
|
||||
Key</button>
|
||||
<form id="cred-form" action="/ui/login/seckey" method="POST">
|
||||
<input hidden="hidden" name="cred" id="cred">
|
||||
|
||||
<button type="button" class="btn btn-dark" id="start-seckey-button"
|
||||
>Use Security Key</button>
|
||||
</form>
|
||||
(% endif %)
|
||||
|
||||
(% endblock %)
|
||||
|
|
Loading…
Reference in a new issue