Files
teleport/lib/auth/webauthn/httpserver/index.html
T
Alan Parra e37760191c Add a toy Webauthn web interface (#8326)
The toy interface serves a few purposes:

Easy testing of Webauthn login and of interop between CLI and Web
Reference for the actual Web UI implementation

* Add a toy Webauthn web interface
* Use the device type to decide whether to use the App ID
* Small tweaks in server and Js
* Fix webauthncli TestLogin breakage
* Replace Printf with Println
2021-09-22 12:15:12 -03:00

190 lines
5.2 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebAuthn Playground</title>
</head>
<body>
<form>
<p>Login and Registration</p>
<label for="user">User:</label>
<input type="text" id="user" name="user"/>
<br/>
<label for="pass">Password:</label>
<input type="password" id="pass" name="pass"/>
<br/>
</form>
<form>
<p>Registration</p>
<label for="devName">Device name:</label>
<input type="text" id="devName" name="devName"/>
<br/>
<label for="totpCode">OTP code:</label>
<input type="text" id="totpCode" name="totpCode"/>
<br/>
</form>
<button onclick="login()">Login</button>
<button onclick="register()">Register</button>
</body>
<script>
// URLBase64 to ArrayBuffer
function bufferDecode(value) {
return Uint8Array.from(atob(value), c => c.charCodeAt(0));
}
// ArrayBuffer to URLBase64
function bufferEncode(value) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(value)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
function register() {
const user = document.getElementById("user").value;
const pass = document.getElementById("pass").value;
const devName = document.getElementById("devName").value;
const totpCode = document.getElementById("totpCode").value;
if (user === "" || pass === "") {
alert("user and password required");
return;
}
if (devName === "" || totpCode === "") {
alert("device information required");
return;
}
const body = JSON.stringify({
"user": user,
"pass": pass,
"dev_name": devName,
"totp_code": totpCode,
})
return fetch("/register/1", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: body
})
.then(res => {
if (res.status === 200) {
return res;
}
throw new Error(res.statusText);
})
.then(res => res.json())
.then(res => {
res.publicKey.challenge = bufferDecode(res.publicKey.challenge);
res.publicKey.user.id = bufferDecode(res.publicKey.user.id);
if (res.publicKey.excludeCredentials) {
res.publicKey.excludeCredentials.forEach(function (x) {
x.id = bufferDecode(x.id);
})
}
console.log("CredentialCreation", res);
return res;
})
.then(res => navigator.credentials.create({publicKey: res.publicKey}))
.then(credential => {
const body = JSON.stringify({
id: credential.id,
rawId: bufferEncode(credential.rawId),
type: credential.type,
response: {
attestationObject: bufferEncode(credential.response.attestationObject),
clientDataJSON: bufferEncode(credential.response.clientDataJSON),
},
});
console.log("CredentialCreationResponse", body);
return fetch("/register/2", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: body,
})
})
.then(res => {
if (res.status === 200) {
alert("registration successful");
} else {
alert("registration failed");
}
});
}
function login() {
const user = document.getElementById("user").value;
const pass = document.getElementById("pass").value;
if (user === "" || pass === "") {
alert("user and password required");
return;
}
const body = JSON.stringify({
"user": user,
"pass": pass,
})
return fetch("/login/1", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: body
})
.then(res => {
if (res.status === 200) {
return res;
}
throw new Error(res.statusText);
})
.then(res => res.json())
.then(res => {
res.publicKey.challenge = bufferDecode(res.publicKey.challenge);
res.publicKey.allowCredentials.forEach(function (x) {
x.id = bufferDecode(x.id);
});
console.log("CredentialAssertion", res);
return res;
})
.then(res => navigator.credentials.get({publicKey: res.publicKey}))
.then(credential => {
const body = JSON.stringify({
user: user,
id: credential.id,
rawId: bufferEncode(credential.rawId),
type: credential.type,
response: {
authenticatorData: bufferEncode(credential.response.authenticatorData),
clientDataJSON: bufferEncode(credential.response.clientDataJSON),
signature: bufferEncode(credential.response.signature),
userHandle: bufferEncode(credential.response.userHandle),
},
});
console.log("CredentialAssertionResponse", body);
return fetch("/login/2", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: body,
});
})
.then(res => {
if (res.status === 200) {
alert("login successful");
} else {
alert("login failed");
}
});
}
// check whether current browser supports WebAuthn
if (!window.PublicKeyCredential) {
alert("Error: this browser does not support WebAuthn");
}
</script>
</html>