Allows login with NIP-05 address

This commit is contained in:
Vitor Pamplona
2024-06-15 12:33:43 -04:00
parent ae0ae413e6
commit 80d506be37
2 changed files with 36 additions and 48 deletions

View File

@@ -25,12 +25,9 @@ import com.vitorpamplona.amethyst.BuildConfig
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Request import okhttp3.Request
import okhttp3.Response
class Nip05NostrAddressVerifier() { class Nip05NostrAddressVerifier {
fun assembleUrl(nip05address: String): String? { fun assembleUrl(nip05address: String): String? {
val parts = nip05address.trim().split("@") val parts = nip05address.trim().split("@")
@@ -46,7 +43,7 @@ class Nip05NostrAddressVerifier() {
suspend fun fetchNip05Json( suspend fun fetchNip05Json(
nip05: String, nip05: String,
onSuccess: (String) -> Unit, onSuccess: suspend (String) -> Unit,
onError: (String) -> Unit, onError: (String) -> Unit,
) = withContext(Dispatchers.IO) { ) = withContext(Dispatchers.IO) {
checkNotInMainThread() checkNotInMainThread()
@@ -60,52 +57,39 @@ class Nip05NostrAddressVerifier() {
try { try {
val request = val request =
Request.Builder() Request
.Builder()
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}") .header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
.url(url) .url(url)
.build() .build()
// Fetchers MUST ignore any HTTP redirects given by the /.well-known/nostr.json endpoint. // Fetchers MUST ignore any HTTP redirects given by the /.well-known/nostr.json endpoint.
HttpClientManager.getHttpClient().newBuilder().followRedirects(false).build() HttpClientManager
.getHttpClient()
.newBuilder()
.followRedirects(false)
.build()
.newCall(request) .newCall(request)
.enqueue( .execute()
object : Callback { .use {
override fun onResponse( checkNotInMainThread()
call: Call,
response: Response,
) {
checkNotInMainThread()
response.use { if (it.isSuccessful) {
if (it.isSuccessful) { onSuccess(it.body.string())
onSuccess(it.body.string()) } else {
} else { onError(
onError( "Could not resolve $nip05. Error: ${it.code}. Check if the server is up and if the address $nip05 is correct",
"Could not resolve $nip05. Error: ${it.code}. Check if the server is up and if the address $nip05 is correct", )
) }
} }
}
}
override fun onFailure(
call: Call,
e: java.io.IOException,
) {
onError(
"Could not resolve $url. Check if the server is up and if the address $nip05 is correct",
)
e.printStackTrace()
}
},
)
} catch (e: Exception) { } catch (e: Exception) {
if (e is CancellationException) throw e if (e is CancellationException) throw e
onError("Could not resolve '$url': ${e.message}") onError("Could not resolve NIP-05 $nip05 as URL $url: ${e.message}")
} }
} }
suspend fun verifyNip05( suspend fun verifyNip05(
nip05: String, nip05: String,
onSuccess: (String) -> Unit, onSuccess: suspend (String) -> Unit,
onError: (String) -> Unit, onError: (String) -> Unit,
) { ) {
// check fails on tests // check fails on tests

View File

@@ -29,6 +29,7 @@ import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.ServiceManager import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.HttpClientManager import com.vitorpamplona.amethyst.service.HttpClientManager
import com.vitorpamplona.amethyst.service.Nip05NostrAddressVerifier
import com.vitorpamplona.amethyst.service.relays.Client import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair import com.vitorpamplona.quartz.crypto.KeyPair
@@ -57,7 +58,7 @@ import java.util.regex.Pattern
val EMAIL_PATTERN = Pattern.compile(".+@.+\\.[a-z]+") val EMAIL_PATTERN = Pattern.compile(".+@.+\\.[a-z]+")
@Stable @Stable
class AccountStateViewModel() : ViewModel() { class AccountStateViewModel : ViewModel() {
var serviceManager: ServiceManager? = null var serviceManager: ServiceManager? = null
private val _accountContent = MutableStateFlow<AccountState>(AccountState.Loading) private val _accountContent = MutableStateFlow<AccountState>(AccountState.Loading)
@@ -144,15 +145,6 @@ class AccountStateViewModel() : ViewModel() {
proxyPort = proxyPort, proxyPort = proxyPort,
signer = NostrSignerInternal(keyPair), signer = NostrSignerInternal(keyPair),
) )
} else if (EMAIL_PATTERN.matcher(key).matches()) {
val keyPair = KeyPair()
// TODO: Evaluate NIP-5
Account(
keyPair,
proxy = proxy,
proxyPort = proxyPort,
signer = NostrSignerInternal(keyPair),
)
} else { } else {
val keyPair = KeyPair(Hex.decode(key)) val keyPair = KeyPair(Hex.decode(key))
Account( Account(
@@ -243,6 +235,18 @@ class AccountStateViewModel() : ViewModel() {
onError(null) onError(null)
} }
} }
} else if (EMAIL_PATTERN.matcher(key).matches()) {
Nip05NostrAddressVerifier().verifyNip05(
key,
onSuccess = { publicKey ->
loginSync(Hex.decode(publicKey).toNpub(), useProxy, proxyPort, loginWithExternalSigner, packageName) {
onError(null)
}
},
onError = {
onError(it)
},
)
} else { } else {
loginSync(key, useProxy, proxyPort, loginWithExternalSigner, packageName) { loginSync(key, useProxy, proxyPort, loginWithExternalSigner, packageName) {
onError(null) onError(null)