Add student account self-service security features and discoverability fixes; v2.1.0
Adds forgot/reset password, signup email verification, sign-out-of-all-devices, note-usage and certificate display in account settings, and password-changed security emails. Fixes study sessions being lost on server restart by persisting them to disk, and fixes sanitizeStudyUsers silently dropping the new pending-token fields on load. Also surfaces the lesson discussion board in the main site nav, trims the sign-in page for returning users, and renames colossiansStudy.tsx/colossiansStudyData.ts to studyPages.tsx/legacyColossiansData.ts now that the study system covers more than Colossians. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a6e8243705
commit
841e754e16
16 files changed
+727
-50
No files matched your search
+22
-1
@@ -22,7 +22,7 @@ import {
|
||||
DOWNLOAD_TOKEN_TTL_MS,
|
||||
} from './config.js'
|
||||
import { state } from './state.js'
|
||||
import { queueStudyRemindersWrite, queueStudyUsersWrite } from './data.js'
|
||||
import { queueStudyRemindersWrite, queueStudyUsersWrite, queueStudySessionsWrite } from './data.js'
|
||||
|
||||
// ── Username / slug normalizers ────────────────────────────────────────────
|
||||
|
||||
@@ -64,6 +64,14 @@ export function hashEmailChangeToken(token) {
|
||||
return createHash('sha256').update(`study-email-change:${String(token)}`).digest('hex')
|
||||
}
|
||||
|
||||
export function hashPasswordResetToken(token) {
|
||||
return createHash('sha256').update(`study-password-reset:${String(token)}`).digest('hex')
|
||||
}
|
||||
|
||||
export function hashEmailVerifyToken(token) {
|
||||
return createHash('sha256').update(`study-email-verify:${String(token)}`).digest('hex')
|
||||
}
|
||||
|
||||
// ── Catalog / enrollment helpers ───────────────────────────────────────────
|
||||
|
||||
export function getStudyCatalog() {
|
||||
@@ -144,9 +152,22 @@ export function getStudyAvatarUrl(subject) {
|
||||
export function createStudySession(userId) {
|
||||
const token = randomUUID()
|
||||
state.studySessions.set(token, { userId, expiresAt: Date.now() + STUDY_SESSION_TTL_MS })
|
||||
queueStudySessionsWrite()
|
||||
return token
|
||||
}
|
||||
|
||||
// Revokes every active session for a user (password change/reset, account deletion, "sign out everywhere").
|
||||
export function invalidateAllStudySessions(userId, exceptToken = null) {
|
||||
let changed = false
|
||||
for (const [token, session] of state.studySessions) {
|
||||
if (session.userId === userId && token !== exceptToken) {
|
||||
state.studySessions.delete(token)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if (changed) queueStudySessionsWrite()
|
||||
}
|
||||
|
||||
export function setStudySessionCookie(res, token) {
|
||||
res.append(
|
||||
'Set-Cookie',
|
||||
|
||||
Reference in new issue
Block a user