mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-02 06:08:23 +08:00
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
190 lines
5.2 KiB
HTML
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>
|