feat: add Nostr Connect login functionality and update routing

- Introduced a new login option in AuthModal for Nostr Connect using remote signer (Primal), enhancing authentication methods.
- Updated the router to include a new path for Nostr Connect callback, allowing for seamless integration after remote signer approval.
- Enhanced error handling in AuthModal to surface Nostr Connect errors, improving user feedback during the login process.

These changes improve the authentication experience by providing additional login options and ensuring robust error management.
This commit is contained in:
Dorian
2026-02-17 04:00:40 +00:00
parent 023653eec5
commit cef73f9694
5 changed files with 225 additions and 2 deletions

View File

@@ -66,6 +66,18 @@
Sign in with Nostr Extension
</button>
<!-- Login with Nostr (Remote Signer / Primal) -->
<button
@click="handleNostrConnectLogin"
:disabled="isLoading"
class="nostr-login-button w-full flex items-center justify-center gap-2 mt-3"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
{{ nostrConnectLoading ? 'Waiting for Primal...' : 'Login with Nostr (Primal)' }}
</button>
<!-- nsec login (hidden by default; tap to reveal field) -->
<template v-if="!showNsecField">
<button
@@ -292,6 +304,7 @@
import { ref, computed, watch } from 'vue'
import { useAuth } from '../composables/useAuth'
import { useAccounts } from '../composables/useAccounts'
import { useNostrConnect } from '../composables/useNostrConnect'
import { accountManager } from '../lib/accounts'
import { authService } from '../services/auth.service'
@@ -313,10 +326,13 @@ const emit = defineEmits<Emits>()
const { loginWithNostr, isLoading: authLoading } = useAuth()
const { loginWithExtension, loginWithPrivateKey } = useAccounts()
const { loginWithRemoteSigner, isLoading: nostrConnectLoading, error: nostrConnectError } = useNostrConnect()
const mode = ref<'login' | 'register' | 'forgot'>(props.defaultMode)
const errorMessage = ref<string | null>(null)
const isLoading = computed(() => authLoading.value || sovereignGenerating.value)
const isLoading = computed(
() => authLoading.value || sovereignGenerating.value || nostrConnectLoading.value,
)
// nsec login (tap to reveal field)
const showNsecField = ref(false)
@@ -353,6 +369,11 @@ watch(() => props.isOpen, (open) => {
}
})
// Surface Nostr Connect errors in the modal
watch(nostrConnectError, (err) => {
if (err) errorMessage.value = err
})
function closeModal() {
emit('close')
errorMessage.value = null
@@ -498,6 +519,23 @@ async function handleNostrLogin() {
}
}
/**
* Login with Nostr via remote signer (Primal, etc.) using nostrconnect:// URI.
* Opens the signer in a new tab, waits for connection, then creates backend session.
*/
async function handleNostrConnectLogin() {
errorMessage.value = null
try {
await loginWithRemoteSigner()
emit('success')
closeModal()
} catch (error: any) {
console.error('Nostr Connect login failed:', error)
errorMessage.value = error?.message || 'Remote signer login failed. Please try again.'
}
}
function cancelNsecField() {
showNsecField.value = false
nsecInput.value = ''