Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a79277f3ed | ||
|
|
521693cfa1 | ||
|
|
3109a93163 | ||
|
|
4505167246 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,3 +19,4 @@ log.txt
|
|||||||
Trash/
|
Trash/
|
||||||
|
|
||||||
nostr-login/
|
nostr-login/
|
||||||
|
nostr-tools/
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
rsync -avz --progress lite/{nostr-lite.js,nostr.bundle.js} ubuntu@laantungir.net:WWW/nostr-login-lite/
|
rsync -avz --chmod=644 --progress lite/{nostr-lite.js,nostr.bundle.js} ubuntu@laantungir.net:WWW/nostr-login-lite/
|
||||||
|
|||||||
@@ -104,6 +104,18 @@
|
|||||||
|
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
// Check for existing authentication state on page load
|
||||||
|
const authState = getAuthState();
|
||||||
|
if (authState && authState.method) {
|
||||||
|
console.log('Found existing authentication:', authState.method);
|
||||||
|
document.getElementById('status').textContent = `Authenticated with: ${authState.method}`;
|
||||||
|
document.getElementById('test-section').style.display = 'block';
|
||||||
|
|
||||||
|
// Store some test data for encryption/decryption
|
||||||
|
window.testCiphertext = null;
|
||||||
|
window.testCiphertext44 = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Listen for authentication events
|
// Listen for authentication events
|
||||||
window.addEventListener('nlMethodSelected', (event) => {
|
window.addEventListener('nlMethodSelected', (event) => {
|
||||||
console.log('User authenticated:', event.detail);
|
console.log('User authenticated:', event.detail);
|
||||||
|
|||||||
@@ -35,6 +35,15 @@
|
|||||||
<!-- Load NOSTR_LOGIN_LITE main library (now includes NIP-46 extension) -->
|
<!-- Load NOSTR_LOGIN_LITE main library (now includes NIP-46 extension) -->
|
||||||
<script src="../lite/nostr-lite.js"></script>
|
<script src="../lite/nostr-lite.js"></script>
|
||||||
|
|
||||||
|
<!-- Load the official nostr-tools bundle first -->
|
||||||
|
<!-- <script src="./nostr.bundle.js"></script> -->
|
||||||
|
<script src="https://laantungir.net/nostr-login-lite/nostr.bundle.js"></script>
|
||||||
|
|
||||||
|
<!-- Load NOSTR_LOGIN_LITE main library -->
|
||||||
|
<script src="https://laantungir.net/nostr-login-lite/nostr-lite.js"></script>
|
||||||
|
<!-- <script src="./nostr-lite.js"></script> -->
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.1.3
|
0.1.7
|
||||||
|
|||||||
821
lite/build.js
821
lite/build.js
File diff suppressed because it is too large
Load Diff
1019
lite/nostr-lite.js
1019
lite/nostr-lite.js
File diff suppressed because it is too large
Load Diff
235
lite/ui/modal.js
235
lite/ui/modal.js
@@ -700,62 +700,8 @@ class Modal {
|
|||||||
_showLocalKeyScreen() {
|
_showLocalKeyScreen() {
|
||||||
this.modalBody.innerHTML = '';
|
this.modalBody.innerHTML = '';
|
||||||
|
|
||||||
const title = document.createElement('h3');
|
|
||||||
title.textContent = 'Local Key';
|
|
||||||
title.style.cssText = 'margin: 0 0 20px 0; font-size: 18px; font-weight: 600;';
|
|
||||||
|
|
||||||
const createButton = document.createElement('button');
|
|
||||||
createButton.textContent = 'Create New Key';
|
|
||||||
createButton.onclick = () => this._createLocalKey();
|
|
||||||
createButton.style.cssText = this._getButtonStyle();
|
|
||||||
|
|
||||||
const importButton = document.createElement('button');
|
|
||||||
importButton.textContent = 'Import Existing Key';
|
|
||||||
importButton.onclick = () => this._showImportKeyForm();
|
|
||||||
importButton.style.cssText = this._getButtonStyle() + 'margin-top: 12px;';
|
|
||||||
|
|
||||||
const backButton = document.createElement('button');
|
|
||||||
backButton.textContent = 'Back';
|
|
||||||
backButton.onclick = () => this._renderLoginOptions();
|
|
||||||
backButton.style.cssText = `
|
|
||||||
display: block;
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 12px;
|
|
||||||
background: #6b7280;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
|
||||||
|
|
||||||
this.modalBody.appendChild(title);
|
|
||||||
this.modalBody.appendChild(createButton);
|
|
||||||
this.modalBody.appendChild(importButton);
|
|
||||||
this.modalBody.appendChild(backButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
_createLocalKey() {
|
|
||||||
try {
|
|
||||||
const sk = window.NostrTools.generateSecretKey();
|
|
||||||
const pk = window.NostrTools.getPublicKey(sk);
|
|
||||||
const nsec = window.NostrTools.nip19.nsecEncode(sk);
|
|
||||||
const npub = window.NostrTools.nip19.npubEncode(pk);
|
|
||||||
|
|
||||||
this._showKeyDisplay(pk, nsec, 'created');
|
|
||||||
} catch (error) {
|
|
||||||
this._showError('Failed to create key: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_showImportKeyForm() {
|
|
||||||
this.modalBody.innerHTML = '';
|
|
||||||
|
|
||||||
const title = document.createElement('h3');
|
|
||||||
title.textContent = 'Import Local Key';
|
|
||||||
title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600;';
|
|
||||||
|
|
||||||
const description = document.createElement('p');
|
const description = document.createElement('p');
|
||||||
description.textContent = 'Enter your secret key in either nsec or hex format:';
|
description.innerHTML = 'Enter your secret key in nsec or hex format, or <span id="generate-new" style="text-decoration: underline; cursor: pointer; color: var(--nl-primary-color);">generate new</span>.';
|
||||||
description.style.cssText = 'margin-bottom: 12px; color: #6b7280; font-size: 14px;';
|
description.style.cssText = 'margin-bottom: 12px; color: #6b7280; font-size: 14px;';
|
||||||
|
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement('textarea');
|
||||||
@@ -777,10 +723,40 @@ class Modal {
|
|||||||
const formatHint = document.createElement('div');
|
const formatHint = document.createElement('div');
|
||||||
formatHint.style.cssText = 'margin-bottom: 16px; font-size: 12px; color: #6b7280; min-height: 16px;';
|
formatHint.style.cssText = 'margin-bottom: 16px; font-size: 12px; color: #6b7280; min-height: 16px;';
|
||||||
|
|
||||||
|
const importButton = document.createElement('button');
|
||||||
|
importButton.textContent = 'Import Key';
|
||||||
|
importButton.disabled = true;
|
||||||
|
importButton.onclick = () => {
|
||||||
|
if (!importButton.disabled) {
|
||||||
|
this._importLocalKey(textarea.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set initial disabled state
|
||||||
|
importButton.style.cssText = `
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: var(--nl-border-width) solid var(--nl-muted-color);
|
||||||
|
border-radius: var(--nl-border-radius);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transition: all 0.2s;
|
||||||
|
font-family: var(--nl-font-family, 'Courier New', monospace);
|
||||||
|
background: var(--nl-secondary-color);
|
||||||
|
color: var(--nl-muted-color);
|
||||||
|
`;
|
||||||
|
|
||||||
textarea.oninput = () => {
|
textarea.oninput = () => {
|
||||||
const value = textarea.value.trim();
|
const value = textarea.value.trim();
|
||||||
if (!value) {
|
if (!value) {
|
||||||
formatHint.textContent = '';
|
formatHint.textContent = '';
|
||||||
|
// Disable button
|
||||||
|
importButton.disabled = true;
|
||||||
|
importButton.style.borderColor = 'var(--nl-muted-color)';
|
||||||
|
importButton.style.color = 'var(--nl-muted-color)';
|
||||||
|
importButton.style.cursor = 'not-allowed';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,32 +764,93 @@ class Modal {
|
|||||||
if (format === 'nsec') {
|
if (format === 'nsec') {
|
||||||
formatHint.textContent = '✅ Valid nsec format detected';
|
formatHint.textContent = '✅ Valid nsec format detected';
|
||||||
formatHint.style.color = '#059669';
|
formatHint.style.color = '#059669';
|
||||||
|
// Enable button
|
||||||
|
importButton.disabled = false;
|
||||||
|
importButton.style.borderColor = 'var(--nl-primary-color)';
|
||||||
|
importButton.style.color = 'var(--nl-primary-color)';
|
||||||
|
importButton.style.cursor = 'pointer';
|
||||||
} else if (format === 'hex') {
|
} else if (format === 'hex') {
|
||||||
formatHint.textContent = '✅ Valid hex format detected';
|
formatHint.textContent = '✅ Valid hex format detected';
|
||||||
formatHint.style.color = '#059669';
|
formatHint.style.color = '#059669';
|
||||||
|
// Enable button
|
||||||
|
importButton.disabled = false;
|
||||||
|
importButton.style.borderColor = 'var(--nl-primary-color)';
|
||||||
|
importButton.style.color = 'var(--nl-primary-color)';
|
||||||
|
importButton.style.cursor = 'pointer';
|
||||||
} else {
|
} else {
|
||||||
formatHint.textContent = '❌ Invalid key format - must be nsec1... or 64-character hex';
|
formatHint.textContent = '❌ Invalid key format - must be nsec1... or 64-character hex';
|
||||||
formatHint.style.color = '#dc2626';
|
formatHint.style.color = '#dc2626';
|
||||||
|
// Disable button
|
||||||
|
importButton.disabled = true;
|
||||||
|
importButton.style.borderColor = 'var(--nl-muted-color)';
|
||||||
|
importButton.style.color = 'var(--nl-muted-color)';
|
||||||
|
importButton.style.cursor = 'not-allowed';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const importButton = document.createElement('button');
|
|
||||||
importButton.textContent = 'Import Key';
|
|
||||||
importButton.onclick = () => this._importLocalKey(textarea.value);
|
|
||||||
importButton.style.cssText = this._getButtonStyle();
|
|
||||||
|
|
||||||
const backButton = document.createElement('button');
|
const backButton = document.createElement('button');
|
||||||
backButton.textContent = 'Back';
|
backButton.textContent = 'Back';
|
||||||
backButton.onclick = () => this._showLocalKeyScreen();
|
backButton.onclick = () => this._renderLoginOptions();
|
||||||
backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;';
|
backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;';
|
||||||
|
|
||||||
this.modalBody.appendChild(title);
|
|
||||||
this.modalBody.appendChild(description);
|
this.modalBody.appendChild(description);
|
||||||
this.modalBody.appendChild(textarea);
|
this.modalBody.appendChild(textarea);
|
||||||
this.modalBody.appendChild(formatHint);
|
this.modalBody.appendChild(formatHint);
|
||||||
this.modalBody.appendChild(importButton);
|
this.modalBody.appendChild(importButton);
|
||||||
this.modalBody.appendChild(backButton);
|
this.modalBody.appendChild(backButton);
|
||||||
|
|
||||||
|
// Add click handler for the "generate new" link
|
||||||
|
const generateLink = document.getElementById('generate-new');
|
||||||
|
if (generateLink) {
|
||||||
|
generateLink.addEventListener('mouseenter', () => {
|
||||||
|
generateLink.style.color = 'var(--nl-accent-color)';
|
||||||
|
});
|
||||||
|
generateLink.addEventListener('mouseleave', () => {
|
||||||
|
generateLink.style.color = 'var(--nl-primary-color)';
|
||||||
|
});
|
||||||
|
generateLink.addEventListener('click', () => {
|
||||||
|
this._generateNewLocalKey(textarea, formatHint);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateNewLocalKey(textarea, formatHint) {
|
||||||
|
try {
|
||||||
|
// Generate a new secret key using NostrTools
|
||||||
|
const sk = window.NostrTools.generateSecretKey();
|
||||||
|
const nsec = window.NostrTools.nip19.nsecEncode(sk);
|
||||||
|
|
||||||
|
// Set the generated key in the textarea
|
||||||
|
textarea.value = nsec;
|
||||||
|
|
||||||
|
// Trigger the oninput event to properly validate and enable the button
|
||||||
|
if (textarea.oninput) {
|
||||||
|
textarea.oninput();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Generated new local secret key (nsec format)');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to generate local key:', error);
|
||||||
|
formatHint.textContent = '❌ Failed to generate key - NostrTools not available';
|
||||||
|
formatHint.style.color = '#dc2626';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_createLocalKey() {
|
||||||
|
try {
|
||||||
|
const sk = window.NostrTools.generateSecretKey();
|
||||||
|
const pk = window.NostrTools.getPublicKey(sk);
|
||||||
|
const nsec = window.NostrTools.nip19.nsecEncode(sk);
|
||||||
|
const npub = window.NostrTools.nip19.npubEncode(pk);
|
||||||
|
|
||||||
|
this._showKeyDisplay(pk, nsec, 'created');
|
||||||
|
} catch (error) {
|
||||||
|
this._showError('Failed to create key: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_detectKeyFormat(keyValue) {
|
_detectKeyFormat(keyValue) {
|
||||||
const trimmed = keyValue.trim();
|
const trimmed = keyValue.trim();
|
||||||
@@ -1094,7 +1131,6 @@ class Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_setAuthMethod(method, options = {}) {
|
_setAuthMethod(method, options = {}) {
|
||||||
// SINGLE-EXTENSION ARCHITECTURE: Handle method switching
|
|
||||||
console.log('Modal: _setAuthMethod called with:', method, options);
|
console.log('Modal: _setAuthMethod called with:', method, options);
|
||||||
|
|
||||||
// CRITICAL: Never install facade for extension methods - leave window.nostr as the extension
|
// CRITICAL: Never install facade for extension methods - leave window.nostr as the extension
|
||||||
@@ -1117,46 +1153,57 @@ class Modal {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-extension methods, we need to ensure WindowNostr facade is available
|
// FOR NON-EXTENSION METHODS: Force-install facade with resilience
|
||||||
console.log('Modal: Non-extension method detected:', method);
|
console.log('Modal: Non-extension method - FORCE-INSTALLING facade with resilience:', method);
|
||||||
|
|
||||||
// Check if we have a preserved extension but no WindowNostr facade installed
|
// Store the current extension if any (for potential restoration later)
|
||||||
const hasPreservedExtension = !!window.NOSTR_LOGIN_LITE?._instance?.preservedExtension;
|
const currentExtension = (window.nostr?.constructor?.name !== 'WindowNostr') ? window.nostr : null;
|
||||||
const hasWindowNostrFacade = window.nostr?.constructor?.name === 'WindowNostr';
|
|
||||||
|
|
||||||
console.log('Modal: Method switching check:');
|
// Get NostrLite instance for facade operations
|
||||||
console.log(' method:', method);
|
|
||||||
console.log(' hasPreservedExtension:', hasPreservedExtension);
|
|
||||||
console.log(' hasWindowNostrFacade:', hasWindowNostrFacade);
|
|
||||||
console.log(' current window.nostr constructor:', window.nostr?.constructor?.name);
|
|
||||||
|
|
||||||
// If we have a preserved extension but no facade, install facade for method switching
|
|
||||||
if (hasPreservedExtension && !hasWindowNostrFacade) {
|
|
||||||
console.log('Modal: Installing WindowNostr facade for method switching (non-extension authentication)');
|
|
||||||
|
|
||||||
// Get the NostrLite instance and install facade with preserved extension
|
|
||||||
const nostrLiteInstance = window.NOSTR_LOGIN_LITE?._instance;
|
const nostrLiteInstance = window.NOSTR_LOGIN_LITE?._instance;
|
||||||
if (nostrLiteInstance && typeof nostrLiteInstance._installFacade === 'function') {
|
if (!nostrLiteInstance || typeof nostrLiteInstance._installFacade !== 'function') {
|
||||||
const preservedExtension = nostrLiteInstance.preservedExtension;
|
|
||||||
console.log('Modal: Installing facade with preserved extension:', preservedExtension?.constructor?.name);
|
|
||||||
|
|
||||||
nostrLiteInstance._installFacade(preservedExtension);
|
|
||||||
console.log('Modal: WindowNostr facade installed for method switching');
|
|
||||||
} else {
|
|
||||||
console.error('Modal: Cannot access NostrLite instance or _installFacade method');
|
console.error('Modal: Cannot access NostrLite instance or _installFacade method');
|
||||||
}
|
// Fallback: emit event anyway
|
||||||
|
const event = new CustomEvent('nlMethodSelected', {
|
||||||
|
detail: { method, ...options }
|
||||||
|
});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
this.close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no extension at all, ensure facade is installed for local/NIP-46/readonly methods
|
// IMMEDIATE FACADE INSTALLATION
|
||||||
else if (!hasPreservedExtension && !hasWindowNostrFacade) {
|
console.log('Modal: Installing WindowNostr facade immediately for method:', method);
|
||||||
console.log('Modal: Installing WindowNostr facade for non-extension methods (no extension detected)');
|
const preservedExtension = nostrLiteInstance.preservedExtension || currentExtension;
|
||||||
|
nostrLiteInstance._installFacade(preservedExtension, true);
|
||||||
|
console.log('Modal: WindowNostr facade force-installed, current window.nostr:', window.nostr?.constructor?.name);
|
||||||
|
|
||||||
const nostrLiteInstance = window.NOSTR_LOGIN_LITE?._instance;
|
// DELAYED FACADE RESILIENCE - Reinstall after extension override attempts
|
||||||
if (nostrLiteInstance && typeof nostrLiteInstance._installFacade === 'function') {
|
const forceReinstallFacade = () => {
|
||||||
nostrLiteInstance._installFacade();
|
console.log('Modal: RESILIENCE CHECK - Current window.nostr after delay:', window.nostr?.constructor?.name);
|
||||||
console.log('Modal: WindowNostr facade installed for non-extension methods');
|
|
||||||
|
// If facade was overridden by extension, reinstall it
|
||||||
|
if (window.nostr?.constructor?.name !== 'WindowNostr') {
|
||||||
|
console.log('Modal: FACADE OVERRIDDEN! Force-reinstalling WindowNostr facade for user choice:', method);
|
||||||
|
nostrLiteInstance._installFacade(preservedExtension, true);
|
||||||
|
console.log('Modal: Resilient facade force-reinstall complete, window.nostr:', window.nostr?.constructor?.name);
|
||||||
|
|
||||||
|
// Schedule another check in case of persistent extension override
|
||||||
|
setTimeout(() => {
|
||||||
|
if (window.nostr?.constructor?.name !== 'WindowNostr') {
|
||||||
|
console.log('Modal: PERSISTENT OVERRIDE! Final facade force-reinstall for method:', method);
|
||||||
|
nostrLiteInstance._installFacade(preservedExtension, true);
|
||||||
}
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
console.log('Modal: Facade persistence verified - no override detected');
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Schedule resilience checks at multiple intervals
|
||||||
|
setTimeout(forceReinstallFacade, 100); // Quick check
|
||||||
|
setTimeout(forceReinstallFacade, 500); // Main check
|
||||||
|
setTimeout(forceReinstallFacade, 1500); // Final check
|
||||||
|
|
||||||
// Emit auth method selection
|
// Emit auth method selection
|
||||||
const event = new CustomEvent('nlMethodSelected', {
|
const event = new CustomEvent('nlMethodSelected', {
|
||||||
|
|||||||
10
nostr_login_lite.code-workspace
Normal file
10
nostr_login_lite.code-workspace
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"liveServer.settings.port": 5501
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user