mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +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 super::{cookies, empty_string_as_none, HtmlTemplate, UnrecoverableErrorView};
|
||||||
|
use crate::https::views::errors::HtmxError;
|
||||||
use crate::https::{
|
use crate::https::{
|
||||||
extractors::{DomainInfo, DomainInfoRead, VerifiedClientInformation},
|
extractors::{DomainInfo, DomainInfoRead, VerifiedClientInformation},
|
||||||
middleware::KOpId,
|
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
|
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(
|
pub async fn view_login_passkey_post(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
|
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
|
||||||
DomainInfo(domain_info): DomainInfo,
|
DomainInfo(domain_info): DomainInfo,
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
Json(assertion): Json<Box<PublicKeyCredential>>,
|
Form(assertion): Form<JsonedPublicKeyCredential>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let auth_cred = AuthCredential::Passkey(assertion);
|
let result = serde_json::from_str::<Box<PublicKeyCredential>>(assertion.cred.as_str());
|
||||||
credential_step(state, kopid, jar, client_auth_info, auth_cred, domain_info).await
|
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(
|
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);
|
let credentialRequestOptions = JSON.parse(document.getElementById('data').textContent);
|
||||||
credentialRequestOptions.publicKey.challenge = Base64.toUint8Array(credentialRequestOptions.publicKey.challenge);
|
credentialRequestOptions.publicKey.challenge = Base64.toUint8Array(credentialRequestOptions.publicKey.challenge);
|
||||||
credentialRequestOptions.publicKey.allowCredentials?.forEach(function (listItem) {
|
credentialRequestOptions.publicKey.allowCredentials?.forEach(function (listItem) {
|
||||||
|
@ -8,46 +8,35 @@ function asskey_login(target) {
|
||||||
|
|
||||||
navigator.credentials.get({ publicKey: credentialRequestOptions.publicKey })
|
navigator.credentials.get({ publicKey: credentialRequestOptions.publicKey })
|
||||||
.then((assertion) => {
|
.then((assertion) => {
|
||||||
const myRequest = new Request(target, {
|
document.getElementById("cred").value = JSON.stringify({
|
||||||
method: 'POST',
|
id: assertion.id,
|
||||||
redirect: 'follow',
|
rawId: Base64.fromUint8Array(new Uint8Array(assertion.rawId), true),
|
||||||
headers: {
|
type: assertion.type,
|
||||||
'Content-Type': 'application/json'
|
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) => {
|
document.getElementById("cred-form").submit();
|
||||||
if (response.redirected) {
|
}).catch((error) => {
|
||||||
window.location.replace(response.url);
|
console.error(`Failed to complete passkey authentication: ${error}`);
|
||||||
return;
|
throw error;
|
||||||
} else {
|
});
|
||||||
console.error("expected a redirect");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const myButton = document.getElementById("start-passkey-button");
|
const myButton = document.getElementById("start-passkey-button");
|
||||||
myButton.addEventListener("click", () => {
|
myButton.addEventListener("click", () => {
|
||||||
asskey_login('/ui/login/passkey');
|
asskey_login();
|
||||||
});
|
});
|
||||||
} catch (_error) {};
|
} catch (_error) {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const myButton = document.getElementById("start-seckey-button");
|
const myButton = document.getElementById("start-seckey-button");
|
||||||
myButton.addEventListener("click", () => {
|
myButton.addEventListener("click", () => {
|
||||||
asskey_login('/ui/login/seckey');
|
asskey_login();
|
||||||
});
|
});
|
||||||
} catch (_error) {};
|
} catch (_error) {};
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,19 @@
|
||||||
defer></script>
|
defer></script>
|
||||||
|
|
||||||
(% if passkey %)
|
(% if passkey %)
|
||||||
<button hx-disable type="button" class="btn btn-dark"
|
<form id="cred-form" action="/ui/login/passkey" method="POST">
|
||||||
id="start-passkey-button">Use Passkey</button>
|
<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 %)
|
(% else %)
|
||||||
<button type="button" class="btn btn-dark" id="start-seckey-button">Use Security
|
<form id="cred-form" action="/ui/login/seckey" method="POST">
|
||||||
Key</button>
|
<input hidden="hidden" name="cred" id="cred">
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-dark" id="start-seckey-button"
|
||||||
|
>Use Security Key</button>
|
||||||
|
</form>
|
||||||
(% endif %)
|
(% endif %)
|
||||||
|
|
||||||
(% endblock %)
|
(% endblock %)
|
||||||
|
|
Loading…
Reference in a new issue