From bb4f13fc6579b66a750fb55935f1130404d117dc Mon Sep 17 00:00:00 2001 From: Dorian Date: Sat, 14 Feb 2026 15:26:42 +0000 Subject: [PATCH] feat: implement nsec login functionality in AuthModal - Added a new nsec login option that allows users to sign in using their private key. - Introduced a toggle to reveal the nsec input field, enhancing user experience. - Implemented validation and error handling for nsec submissions, ensuring robust login flow. - Updated styles and layout for better visual consistency and usability. These changes enhance the authentication process by providing an additional secure login method for users. --- src/components/AuthModal.vue | 140 ++++++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 25 deletions(-) diff --git a/src/components/AuthModal.vue b/src/components/AuthModal.vue index ab2eb25..857cdae 100644 --- a/src/components/AuthModal.vue +++ b/src/components/AuthModal.vue @@ -49,26 +49,16 @@
Forgot password?
- - -
-
-
-
-
- or -
-
- - -
-
-
-
-
- or + + +
+ +
+ +
- + +
@@ -291,13 +312,17 @@ const props = withDefaults(defineProps(), { const emit = defineEmits() const { loginWithNostr, isLoading: authLoading } = useAuth() -const { loginWithExtension } = useAccounts() +const { loginWithExtension, loginWithPrivateKey } = useAccounts() const mode = ref<'login' | 'register' | 'forgot'>(props.defaultMode) const errorMessage = ref(null) const isLoading = computed(() => authLoading.value || sovereignGenerating.value) -// Amber login state +// nsec login (tap to reveal field) +const showNsecField = ref(false) +const nsecInput = ref('') + +// Amber login state (hidden for now) const amberPhase = ref<'idle' | 'waiting-pubkey' | 'waiting-signature' | 'completing'>('idle') const amberPubkey = ref(null) const amberUnsignedEvent = ref(null) @@ -322,6 +347,8 @@ watch(() => props.isOpen, (open) => { sovereignDismissed.value = false generatedKeys.value = null errorMessage.value = null + showNsecField.value = false + nsecInput.value = '' cancelAmber() } }) @@ -471,6 +498,33 @@ async function handleNostrLogin() { } } +function cancelNsecField() { + showNsecField.value = false + nsecInput.value = '' + errorMessage.value = null +} + +/** + * Sign in with pasted nsec. Adds account, sets active, then creates backend session. + */ +async function handleNsecSubmit() { + const nsec = nsecInput.value.trim() + if (!nsec) return + errorMessage.value = null + try { + await loginWithPrivateKey(nsec) + const account = accountManager.active + if (!account) throw new Error('Account not set') + await loginWithNostr(account.pubkey, 'nsec', {}, undefined) + nsecInput.value = '' + showNsecField.value = false + emit('success') + closeModal() + } catch (err: any) { + errorMessage.value = err.message || 'Invalid nsec or sign-in failed.' + } +} + /** * Clean up Amber flow state, timers, and event listeners. */ @@ -809,6 +863,42 @@ declare global { color: rgba(255, 255, 255, 0.3); } +/* nsec login field block */ +.nsec-field-block { + padding: 0.5rem 0; +} +.nsec-hint { + font-size: 0.8125rem; + color: rgba(255, 255, 255, 0.5); + margin-bottom: 0.5rem; + line-height: 1.4; +} +.nsec-field-block .auth-input { + margin-bottom: 0.5rem; +} +.nsec-actions { + display: flex; + gap: 0.5rem; + align-items: center; + margin-top: 0.5rem; +} +.nsec-cancel-btn { + padding: 0.5rem 1rem; + font-size: 0.875rem; + font-weight: 500; + color: rgba(255, 255, 255, 0.6); + background: transparent; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + cursor: pointer; + transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease; +} +.nsec-cancel-btn:hover { + color: rgba(255, 255, 255, 0.9); + border-color: rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.05); +} + /* Modal Transitions */ .modal-fade-enter-active, .modal-fade-leave-active {