122 lines
3.5 KiB
TypeScript
122 lines
3.5 KiB
TypeScript
import { Layout } from "./Layout.tsx";
|
|
|
|
export const LoginPage = () => {
|
|
return (
|
|
<Layout title="Login">
|
|
<div style={{ textAlign: "center" }}>
|
|
<h1 style={{ marginBottom: "0.5rem" }}>Authenticate</h1>
|
|
<p style={{ color: "#666", marginBottom: "2rem" }}>
|
|
Use your registered hardware key or passkey to log in.
|
|
</p>
|
|
|
|
<div
|
|
id="instructionBox"
|
|
style={{
|
|
background: "#eef2f5",
|
|
padding: "1rem",
|
|
borderRadius: "6px",
|
|
marginBottom: "1.5rem",
|
|
fontSize: "0.9rem",
|
|
color: "#333",
|
|
border: "1px solid #dcdcdc",
|
|
}}
|
|
>
|
|
<p style={{ margin: "0 0 0.5rem 0" }}>
|
|
<strong>Instruction:</strong>
|
|
</p>
|
|
<ul style={{ margin: 0, paddingLeft: "1.5rem", textAlign: "left" }}>
|
|
<li>
|
|
Insert your hardware token (e.g. YubiKey) into the USB port.
|
|
</li>
|
|
<li>
|
|
Or prepare to scan a QR code if using a mobile device passkey.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style={{ marginBottom: "1rem" }}>
|
|
<input
|
|
type="text"
|
|
id="loginUsername"
|
|
placeholder="Username (optional for passkeys)"
|
|
style={{
|
|
padding: "0.5rem",
|
|
width: "100%",
|
|
maxWidth: "300px",
|
|
borderRadius: "4px",
|
|
border: "1px solid #ccc",
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
id="loginBtn"
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
gap: "0.5rem",
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
|
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
|
</svg>
|
|
Login with Passkey
|
|
</button>
|
|
|
|
<div
|
|
id="loadingIndicator"
|
|
style={{
|
|
display: "none",
|
|
marginTop: "1rem",
|
|
color: "#007bff",
|
|
fontSize: "0.9rem",
|
|
}}
|
|
>
|
|
Waiting for authenticator... Please follow the prompt.
|
|
</div>
|
|
|
|
<div id="statusMessage" class="error"></div>
|
|
|
|
<div class="links" style={{ marginTop: "2rem" }}>
|
|
Don't have an account? <a href="/register">Register here</a>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/public/auth-client.js?v=4"></script>
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
document.getElementById('loginBtn').addEventListener('click', async () => {
|
|
document.getElementById('loadingIndicator').style.display = 'block';
|
|
document.getElementById('loginBtn').disabled = true;
|
|
document.getElementById('statusMessage').textContent = '';
|
|
|
|
try {
|
|
const username = document.getElementById('loginUsername').value;
|
|
await startWebAuthnLogin(username);
|
|
} finally {
|
|
document.getElementById('loadingIndicator').style.display = 'none';
|
|
document.getElementById('loginBtn').disabled = false;
|
|
}
|
|
});
|
|
`,
|
|
}}
|
|
>
|
|
</script>
|
|
</Layout>
|
|
);
|
|
};
|