One way in: email sign-in only; wallet-only sessions finish setup by email
- Remove the "sign in with just your wallet" door from the sign-in card. - A wallet-only session (no account) is walked to the email card with a finish-setup note; verifying the code links that wallet to the account and retires the wallet-only session, so member #, purchases and payouts stay attached and username/profile work. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+5
-11
@@ -467,9 +467,13 @@
|
|||||||
|
|
||||||
async function render() {
|
async function render() {
|
||||||
const me = await IAP.refreshNavWallet();
|
const me = await IAP.refreshNavWallet();
|
||||||
const signedIn = me && me.signedIn;
|
// one way in: email. A wallet-only session (no account) is sent back to the
|
||||||
|
// email card with a finish-setup note; verifying the code links that wallet.
|
||||||
|
const walletOnly = !!(me && me.signedIn && !me.email);
|
||||||
|
const signedIn = me && me.signedIn && !walletOnly;
|
||||||
$('authArea').hidden = !!signedIn;
|
$('authArea').hidden = !!signedIn;
|
||||||
$('memberArea').hidden = !signedIn;
|
$('memberArea').hidden = !signedIn;
|
||||||
|
if ($('mcFinish')) $('mcFinish').hidden = !walletOnly;
|
||||||
if (!signedIn) return;
|
if (!signedIn) return;
|
||||||
if ($('adminLink')) $('adminLink').hidden = !me.isAdmin; // admin portal link, only for ADMIN_EMAIL
|
if ($('adminLink')) $('adminLink').hidden = !me.isAdmin; // admin portal link, only for ADMIN_EMAIL
|
||||||
setPane(location.hash.slice(1) || 'overview');
|
setPane(location.hash.slice(1) || 'overview');
|
||||||
@@ -1403,16 +1407,6 @@
|
|||||||
if (!(await showGauntlet())) await showLoginAd(); // welcome tour outranks the login ad
|
if (!(await showGauntlet())) await showLoginAd(); // welcome tour outranks the login ad
|
||||||
await render();
|
await render();
|
||||||
}));
|
}));
|
||||||
$('walletSigninLink').addEventListener('click', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
try {
|
|
||||||
IAP.status('Check your wallet for the free sign-in signature…');
|
|
||||||
await IAPWallet.signIn();
|
|
||||||
IAP.status('Signed in with your wallet.', 'ok');
|
|
||||||
if (!(await showGauntlet())) await showLoginAd(); // welcome tour outranks the login ad
|
|
||||||
await render();
|
|
||||||
} catch (err) { IAP.status((err && err.message) || String(err), 'bad'); }
|
|
||||||
});
|
|
||||||
$('linkBtn').addEventListener('click', busy($('linkBtn'), async () => {
|
$('linkBtn').addEventListener('click', busy($('linkBtn'), async () => {
|
||||||
IAP.status('Check your wallet for the free link signature…');
|
IAP.status('Check your wallet for the free link signature…');
|
||||||
await IAPWallet.signIn(); // server binds the wallet to the signed-in email account
|
await IAPWallet.signIn(); // server binds the wallet to the signed-in email account
|
||||||
|
|||||||
+3
-3
@@ -27,6 +27,8 @@
|
|||||||
<b id="sponsorNoteName" style="font-size:19px;color:var(--mint)"></b></p>
|
<b id="sponsorNoteName" style="font-size:19px;color:var(--mint)"></b></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card" id="magicCard" hidden>
|
<div class="card" id="magicCard" hidden>
|
||||||
|
<p id="mcFinish" class="small" style="color:var(--amber);margin:0 0 10px" hidden>Your wallet is signed in, but it has no account yet.
|
||||||
|
Add your email to finish setting up. Your wallet, member number and purchases stay attached.</p>
|
||||||
<h3>Sign in or join free</h3>
|
<h3>Sign in or join free</h3>
|
||||||
<p class="muted small">Type your email and we send a 6-digit code. No password to invent,
|
<p class="muted small">Type your email and we send a 6-digit code. No password to invent,
|
||||||
no password to forget. New emails get a free account automatically.</p>
|
no password to forget. New emails get a free account automatically.</p>
|
||||||
@@ -50,8 +52,6 @@
|
|||||||
<button class="btn sec" id="loginBtn">Log in</button>
|
<button class="btn sec" id="loginBtn">Log in</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="small muted">Crypto-native? You can also <a href="#" id="walletSigninLink">sign in with just your wallet</a>.
|
|
||||||
One free signature, no email needed.</p>
|
|
||||||
<p class="small muted"><a href="/">← Back to the site</a></p>
|
<p class="small muted"><a href="/">← Back to the site</a></p>
|
||||||
<div class="card" id="adSlotLogin" hidden></div>
|
<div class="card" id="adSlotLogin" hidden></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -697,7 +697,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260909a"></script>
|
<script src="/assets/common.js?v=20260909a"></script>
|
||||||
<script src="/assets/wallet.js?v=20260908t"></script>
|
<script src="/assets/wallet.js?v=20260908t"></script>
|
||||||
<script src="/assets/my.js?v=20260909a"></script>
|
<script src="/assets/my.js?v=20260909b"></script>
|
||||||
<script src="/assets/chat.js?v=20260907l"></script>
|
<script src="/assets/chat.js?v=20260907l"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -568,6 +568,16 @@ const server = http.createServer(async (req, res) => {
|
|||||||
const ref = parseCookies(req)['iap.sponsor'] || '';
|
const ref = parseCookies(req)['iap.sponsor'] || '';
|
||||||
const r = await accounts.ensure(e, ref); // first touch wins; existing accounts unchanged
|
const r = await accounts.ensure(e, ref); // first touch wins; existing accounts unchanged
|
||||||
if (r.error) return json(res, 400, r);
|
if (r.error) return json(res, 400, r);
|
||||||
|
// a wallet-only session (signed with a wallet, no account) finishing setup:
|
||||||
|
// adopt that wallet into the email account so member #, purchases and
|
||||||
|
// payouts stay attached, then retire the wallet-only session
|
||||||
|
const prior = await auth.fromRequest(req);
|
||||||
|
if (prior && prior.address && !prior.email) {
|
||||||
|
const lr = await accounts.linkWallet(e, prior.address);
|
||||||
|
if (lr.error) return json(res, 400, lr);
|
||||||
|
r.account = lr.account || await accounts.byEmail(e);
|
||||||
|
await auth.logout(req);
|
||||||
|
}
|
||||||
if (r.created) { sendWelcome(e, ref).catch(() => {}); } // sponsor notified at username set (/api/my/profile)
|
if (r.created) { sendWelcome(e, ref).catch(() => {}); } // sponsor notified at username set (/api/my/profile)
|
||||||
if (r.created && b.newsletter) sendy.subscribe(r.account.email, r.account.username || '').catch(() => {}); // pre-checked opt-in, silent, new joins only
|
if (r.created && b.newsletter) sendy.subscribe(r.account.email, r.account.username || '').catch(() => {}); // pre-checked opt-in, silent, new joins only
|
||||||
let memberId = 0;
|
let memberId = 0;
|
||||||
@@ -577,7 +587,8 @@ const server = http.createServer(async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -- wallet auth: link-to-account when an email session exists, or
|
// -- wallet auth: link-to-account when an email session exists, or
|
||||||
// wallet-first sign-in for crypto-native users
|
// wallet-first sign-in (no UI door since 2026-09-09; a wallet-only session
|
||||||
|
// is walked to the email card, which adopts the wallet on verify)
|
||||||
if (p === '/api/auth/challenge' && req.method === 'POST') {
|
if (p === '/api/auth/challenge' && req.method === 'POST') {
|
||||||
const b = await readBody(req);
|
const b = await readBody(req);
|
||||||
const r = auth.makeChallenge(b.address);
|
const r = auth.makeChallenge(b.address);
|
||||||
|
|||||||
Reference in New Issue
Block a user