1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JTA Login</title>
<link rel="stylesheet" href="style.css">
<style>
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 1rem;
background: var(--bg);
}
.card {
width: min(520px, 100%);
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 1.5rem;
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.38);
}
h1 {
margin: 0 0 0.25rem 0;
text-align: center;
font-family: var(--font-heading);
font-size: var(--step-2);
line-height: 1.15;
font-weight: 700;
}
p {
margin: 0.5rem 0 1.2rem 0;
text-align: center;
color: var(--text-soft);
line-height: 1.5;
}
.actions {
display: flex;
justify-content: center;
gap: 0.75rem;
}
button {
appearance: none;
border: 1px solid var(--border);
border-radius: 12px;
background: #0f172a;
color: var(--text);
padding: 0.65rem 1rem;
font-size: var(--step-0);
font-weight: 600;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
button:hover {
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}
#status {
margin-top: 1rem;
text-align: center;
min-height: 1.25rem;
font-size: var(--step--1);
color: var(--text-soft);
}
</style>
</head>
<body>
<div class="card">
<h1>JTA Portal</h1>
<p>Public landing page. Login is required to view assignments and all other pages.</p>
<div class="actions">
<button id="login-btn" type="button">Login</button>
</div>
<div id="status" aria-live="polite"></div>
</div>
<script>
const authCookieName = "jta_auth";
const status = document.getElementById("status");
const loginButton = document.getElementById("login-btn");
function toHex(buffer) {
const bytes = new Uint8Array(buffer);
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
async function sha256Hex(input) {
const data = new TextEncoder().encode(input);
const digest = await crypto.subtle.digest("SHA-256", data);
return toHex(digest);
}
function setAuthCookie(hashValue) {
const maxAge = 10 * 365 * 24 * 60 * 60;
document.cookie = `${authCookieName}=${hashValue}; Path=/; Max-Age=${maxAge}; SameSite=Lax`;
}
async function loginWithPrompt() {
const password = prompt("Enter password");
if (password === null) {
status.textContent = "Login cancelled.";
return;
}
status.textContent = "Logging in...";
const hashValue = await sha256Hex(password);
setAuthCookie(hashValue);
status.textContent = "Success. Redirecting...";
window.location.href = "/assignments";
}
loginButton.addEventListener("click", () => {
loginWithPrompt().catch(() => {
status.textContent = "Network error while logging in.";
});
});
</script>
</body>
</html>
|