commit 749364189676eda0465b0a9799227b0fdf31d226
parent 937009167f7814f860575f6d29a60a382fcfc12e
Author: William Casarin <jb55@jb55.com>
Date: Fri, 18 Nov 2022 17:51:01 -0800
Initial NIP05 code
Also add the ability to login with a NIP05 id
Diffstat:
2 files changed, 119 insertions(+), 44 deletions(-)
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
@@ -9,7 +9,7 @@ import SwiftUI
import Starscream
import Kingfisher
-let BOOTSTRAP_RELAYS = [
+var BOOTSTRAP_RELAYS = [
"wss://relay.damus.io",
"wss://nostr-relay.wlvs.space",
"wss://nostr.oxtr.dev",
diff --git a/damus/Views/LoginView.swift b/damus/Views/LoginView.swift
@@ -11,14 +11,19 @@ enum ParsedKey {
case pub(String)
case priv(String)
case hex(String)
-
+ case nip05(String)
+
var is_pub: Bool {
if case .pub = self {
return true
}
+
+ if case .nip05 = self {
+ return true
+ }
return false
}
-
+
var is_hex: Bool {
if case .hex = self {
return true
@@ -31,19 +36,68 @@ struct LoginView: View {
@State var key: String = ""
@State var is_pubkey: Bool = false
@State var error: String? = nil
-
+
func get_error(parsed_key: ParsedKey?) -> String? {
if self.error != nil {
return self.error
}
-
+
if !key.isEmpty && parsed_key == nil {
return "Invalid key"
}
-
+
return nil
}
-
+
+ func process_login(_ key: ParsedKey, is_pubkey: Bool) -> Bool {
+ switch key {
+ case .priv(let priv):
+ save_privkey(privkey: priv)
+ guard let pk = privkey_to_pubkey(privkey: priv) else {
+ return false
+ }
+ save_pubkey(pubkey: pk)
+
+ case .pub(let pub):
+ clear_saved_privkey()
+ save_pubkey(pubkey: pub)
+
+ case .nip05(let id):
+ Task.init {
+ guard let nip05 = await get_nip05_pubkey(id: id) else {
+ self.error = "Could not fetch pubkey"
+ return
+ }
+
+ for relay in nip05.relays {
+ if !(BOOTSTRAP_RELAYS.contains { $0 == relay }) {
+ BOOTSTRAP_RELAYS.append(relay)
+ }
+ }
+ save_pubkey(pubkey: nip05.pubkey)
+
+ notify(.login, ())
+ }
+
+
+ case .hex(let hexstr):
+ if is_pubkey {
+ clear_saved_privkey()
+ save_pubkey(pubkey: hexstr)
+ } else {
+ save_privkey(privkey: hexstr)
+ guard let pk = privkey_to_pubkey(privkey: hexstr) else {
+ return false
+ }
+ save_pubkey(pubkey: pk)
+ }
+ }
+
+ notify(.login, ())
+ return true
+}
+
+
var body: some View {
ZStack(alignment: .top) {
DamusGradient()
@@ -52,15 +106,15 @@ struct LoginView: View {
.foregroundColor(.white)
.font(.title)
.padding()
-
+
Text("Enter your account key to login:")
.foregroundColor(.white)
.padding()
-
+
KeyInput("nsec1...", key: $key)
-
+
let parsed = parse_key(key)
-
+
if parsed?.is_hex ?? false {
Text("This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key.")
.font(.subheadline.bold())
@@ -68,19 +122,19 @@ struct LoginView: View {
PubkeySwitch(isOn: $is_pubkey)
.padding()
}
-
+
if let error = get_error(parsed_key: parsed) {
Text(error)
.foregroundColor(.red)
.padding()
}
-
+
if parsed?.is_pub ?? false {
Text("This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective.")
.foregroundColor(.white)
.padding()
}
-
+
if let p = parsed {
DamusWhiteButton("Login") {
if !process_login(p, is_pubkey: self.is_pubkey) {
@@ -113,10 +167,15 @@ func parse_key(_ thekey: String) -> ParsedKey? {
if key.count > 0 && key.first! == "@" {
key = String(key.dropFirst())
}
+
if hex_decode(key) != nil {
return .hex(key)
}
-
+
+ if (key.contains { $0 == "@" }) {
+ return .nip05(key)
+ }
+
if let bech_key = decode_bech32_key(key) {
switch bech_key {
case .pub(let pk):
@@ -125,49 +184,65 @@ func parse_key(_ thekey: String) -> ParsedKey? {
return .priv(sec)
}
}
-
+
return nil
}
-func process_login(_ key: ParsedKey, is_pubkey: Bool) -> Bool {
- switch key {
- case .priv(let priv):
- save_privkey(privkey: priv)
- guard let pk = privkey_to_pubkey(privkey: priv) else {
- return false
- }
- save_pubkey(pubkey: pk)
-
- case .pub(let pub):
- clear_saved_privkey()
- save_pubkey(pubkey: pub)
-
- case .hex(let hexstr):
- if is_pubkey {
- clear_saved_privkey()
- save_pubkey(pubkey: hexstr)
- } else {
- save_privkey(privkey: hexstr)
- guard let pk = privkey_to_pubkey(privkey: hexstr) else {
- return false
- }
- save_pubkey(pubkey: pk)
+struct NIP05Result: Decodable {
+ let names: Dictionary<String, String>
+ let relays: Dictionary<String, [String]>?
+}
+
+struct NIP05User {
+ let pubkey: String
+ let relays: [String]
+}
+
+func get_nip05_pubkey(id: String) async -> NIP05User? {
+ let parts = id.components(separatedBy: "@")
+
+ guard parts.count == 2 else {
+ return nil
+ }
+
+ let user = parts[0]
+ let host = parts[1]
+
+ guard let url = URL(string: "https://\(host)/.well-known/nostr.json?name=\(user)") else {
+ return nil
+ }
+
+ guard let (data, _) = try? await URLSession.shared.data(for: URLRequest(url: url)) else {
+ return nil
+ }
+
+ guard let json: NIP05Result = decode_data(data) else {
+ return nil
+ }
+
+ guard let pubkey = json.names[user] else {
+ return nil
+ }
+
+ var relays: [String] = []
+ if let rs = json.relays {
+ if let rs = rs[user] {
+ relays = rs
}
}
-
- notify(.login, ())
- return true
+
+ return NIP05User(pubkey: pubkey, relays: relays)
}
struct KeyInput: View {
let title: String
let key: Binding<String>
-
+
init(_ title: String, key: Binding<String>) {
self.title = title
self.key = key
}
-
+
var body: some View {
TextField("", text: key)
.placeholder(when: key.wrappedValue.isEmpty) {