mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-24 17:48:34 +02:00
feat: decaveat Phase 2 — subscription->tier data source + robust client signing
Caveat 2 (premium sub had no data source) -> RESOLVED: - src/services/subscriptionService.js: resolves a premium tier by npub. Two sources, both optional (absent => null => unchanged): PREMIUM_NPUBS operator allowlist (npub1/hex[:tier]) works TODAY with zero backend dependency, and an optional SMIRK_PREMIUM_STATUS_URL HTTP endpoint for full automation. Cached. - identityService.entitlementsForUser populates user.subscription_tier from it (by smirk_public_key) before the snapshot, so an active sub unlocks cosmetics. (The Smirk backend's /premium/status is self-only, so a by-npub service lookup is the one remaining backend addition for full automation — documented.) Caveat 1 (client signing was bare scaffold) -> HARDENED: - html/js/smirkChatSign.js now does the Nostr-scope-grant retry (sign first; on NOT_AUTHORIZED, getNostrPublicKey() once + retry), same as SmirkAuth — so a returning user signs with one approval instead of silently falling back. Still feature-detected + graceful. A real Smirk wallet click is the last confirm. Docs updated (MONETIZATION.md, CHAT_AND_NOSTR.md). Tests: +6 (allowlist hex/npub1, HTTP active/inactive+cache, identityService unlock, non-premium unchanged). Suite 57 suites / 413 tests green.
This commit is contained in:
@@ -8,6 +8,21 @@
|
||||
// unavailable or declined — in which case the caller falls back to the normal unsigned send.
|
||||
// Feature-detected end to end, so it's a no-op (always false) until the page has a connected
|
||||
// Smirk wallet; wiring it into a send site is therefore always safe.
|
||||
// Sign an event, granting the Nostr scope on demand. Signing FIRST means a returning user
|
||||
// (scope already granted for this origin — e.g. from the game's NIP-98 login) gets a single
|
||||
// approval; only a first-time origin hits NOT_AUTHORIZED and needs the one-time scope grant.
|
||||
// Same pattern as SmirkAuth (html/js/network/smirkAuth.js).
|
||||
function signWithScope(smirk, tmpl) {
|
||||
return Promise.resolve()
|
||||
.then(function () { return smirk.signNostrEvent(tmpl); })
|
||||
.catch(function (err) {
|
||||
var needsScope = (err && err.code === 'NOT_AUTHORIZED')
|
||||
|| /nostr scope|getNostrPublicKey/i.test((err && err.message) || '');
|
||||
if (!needsScope || typeof smirk.getNostrPublicKey !== 'function') throw err;
|
||||
return smirk.getNostrPublicKey().then(function () { return smirk.signNostrEvent(tmpl); });
|
||||
});
|
||||
}
|
||||
|
||||
function smirkChatSign(socket, text, tag) {
|
||||
try {
|
||||
var smirk = root.smirk;
|
||||
@@ -18,7 +33,7 @@
|
||||
tags: [['t', tag || 'wowngeon-global']],
|
||||
content: String(text)
|
||||
};
|
||||
return Promise.resolve(smirk.signNostrEvent(tmpl)).then(function (signed) {
|
||||
return signWithScope(smirk, tmpl).then(function (signed) {
|
||||
if (!signed || !signed.id || !signed.sig || !signed.pubkey) return false;
|
||||
socket.emit('chat_signed', { event: signed });
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user