diff --git a/account.html b/account.html
index ef8858a..04f9e24 100644
--- a/account.html
+++ b/account.html
@@ -41,13 +41,38 @@
Pod
Explore
+ Password
+ Climb from rung 1 (default me) to rung 2 (your password).
+
+
+
Session
@@ -110,6 +135,72 @@
Session
fail('Sign-out failed: ' + (e.message || String(e)));
}
});
+
+ // Change-password flow. POSTs to JSS's PUT /idp/credentials with
+ // {currentPassword, newPassword}. The endpoint requires the
+ // session's DPoP-bound bearer token plus re-proof of the current
+ // password — session.authFetch handles the auth headers, the form
+ // collects the re-auth proof.
+ const pwToggle = document.getElementById('pw-toggle');
+ const pwForm = document.getElementById('pw-form');
+ const pwCancel = document.getElementById('pw-cancel');
+ const pwSubmit = document.getElementById('pw-submit');
+ const pwMsg = document.getElementById('pw-msg');
+ const pwCurrent = document.getElementById('pw-current');
+ const pwNew = document.getElementById('pw-new');
+ const pwConfirm = document.getElementById('pw-confirm');
+
+ function pwStatus(text, isError) {
+ pwMsg.textContent = text;
+ pwMsg.style.display = 'block';
+ pwMsg.style.background = isError ? '#fff0f0' : '#e8f5e9';
+ pwMsg.style.color = isError ? '#a00' : '#1b5e20';
+ }
+ function pwReset() {
+ pwForm.reset();
+ pwMsg.style.display = 'none';
+ }
+
+ pwToggle.addEventListener('click', () => {
+ pwForm.hidden = false;
+ pwToggle.hidden = true;
+ pwCurrent.focus();
+ });
+ pwCancel.addEventListener('click', () => {
+ pwForm.hidden = true;
+ pwToggle.hidden = false;
+ pwReset();
+ });
+ pwForm.addEventListener('submit', async (ev) => {
+ ev.preventDefault();
+ if (pwNew.value !== pwConfirm.value) {
+ pwStatus('New password and confirmation do not match.', true);
+ return;
+ }
+ pwSubmit.disabled = true;
+ pwStatus('Updating…');
+ try {
+ const res = await session.authFetch(new URL('./idp/credentials', window.location.href).href, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ currentPassword: pwCurrent.value, newPassword: pwNew.value })
+ });
+ if (!res.ok) {
+ let detail = '';
+ try {
+ const j = await res.json();
+ detail = j.error_description || j.error || '';
+ } catch {}
+ throw new Error('HTTP ' + res.status + (detail ? ' — ' + detail : ''));
+ }
+ pwStatus('Password updated. You\'re on rung 2 now.');
+ pwCurrent.value = pwNew.value = pwConfirm.value = '';
+ } catch (e) {
+ pwStatus('Change failed: ' + (e.message || String(e)), true);
+ } finally {
+ pwSubmit.disabled = false;
+ }
+ });
} else {
signedOutEl.hidden = false;
}
diff --git a/package.json b/package.json
index 235d8a8..ff8a11a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "jspod",
- "version": "0.0.18",
+ "version": "0.0.19",
"description": "JavaScript Solid Pod - Just works, batteries included",
"type": "module",
"main": "index.js",