Initial NIP05 code
Also add the ability to login with a NIP05 id
This commit is contained in:
@@ -9,7 +9,7 @@ import SwiftUI
|
|||||||
import Starscream
|
import Starscream
|
||||||
import Kingfisher
|
import Kingfisher
|
||||||
|
|
||||||
let BOOTSTRAP_RELAYS = [
|
var BOOTSTRAP_RELAYS = [
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
"wss://nostr-relay.wlvs.space",
|
"wss://nostr-relay.wlvs.space",
|
||||||
"wss://nostr.oxtr.dev",
|
"wss://nostr.oxtr.dev",
|
||||||
|
|||||||
@@ -11,14 +11,19 @@ enum ParsedKey {
|
|||||||
case pub(String)
|
case pub(String)
|
||||||
case priv(String)
|
case priv(String)
|
||||||
case hex(String)
|
case hex(String)
|
||||||
|
case nip05(String)
|
||||||
|
|
||||||
var is_pub: Bool {
|
var is_pub: Bool {
|
||||||
if case .pub = self {
|
if case .pub = self {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case .nip05 = self {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_hex: Bool {
|
var is_hex: Bool {
|
||||||
if case .hex = self {
|
if case .hex = self {
|
||||||
return true
|
return true
|
||||||
@@ -31,19 +36,68 @@ struct LoginView: View {
|
|||||||
@State var key: String = ""
|
@State var key: String = ""
|
||||||
@State var is_pubkey: Bool = false
|
@State var is_pubkey: Bool = false
|
||||||
@State var error: String? = nil
|
@State var error: String? = nil
|
||||||
|
|
||||||
func get_error(parsed_key: ParsedKey?) -> String? {
|
func get_error(parsed_key: ParsedKey?) -> String? {
|
||||||
if self.error != nil {
|
if self.error != nil {
|
||||||
return self.error
|
return self.error
|
||||||
}
|
}
|
||||||
|
|
||||||
if !key.isEmpty && parsed_key == nil {
|
if !key.isEmpty && parsed_key == nil {
|
||||||
return "Invalid key"
|
return "Invalid key"
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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 {
|
var body: some View {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
DamusGradient()
|
DamusGradient()
|
||||||
@@ -52,15 +106,15 @@ struct LoginView: View {
|
|||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.font(.title)
|
.font(.title)
|
||||||
.padding()
|
.padding()
|
||||||
|
|
||||||
Text("Enter your account key to login:")
|
Text("Enter your account key to login:")
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding()
|
.padding()
|
||||||
|
|
||||||
KeyInput("nsec1...", key: $key)
|
KeyInput("nsec1...", key: $key)
|
||||||
|
|
||||||
let parsed = parse_key(key)
|
let parsed = parse_key(key)
|
||||||
|
|
||||||
if parsed?.is_hex ?? false {
|
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.")
|
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())
|
.font(.subheadline.bold())
|
||||||
@@ -68,19 +122,19 @@ struct LoginView: View {
|
|||||||
PubkeySwitch(isOn: $is_pubkey)
|
PubkeySwitch(isOn: $is_pubkey)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let error = get_error(parsed_key: parsed) {
|
if let error = get_error(parsed_key: parsed) {
|
||||||
Text(error)
|
Text(error)
|
||||||
.foregroundColor(.red)
|
.foregroundColor(.red)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsed?.is_pub ?? false {
|
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.")
|
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)
|
.foregroundColor(.white)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let p = parsed {
|
if let p = parsed {
|
||||||
DamusWhiteButton("Login") {
|
DamusWhiteButton("Login") {
|
||||||
if !process_login(p, is_pubkey: self.is_pubkey) {
|
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! == "@" {
|
if key.count > 0 && key.first! == "@" {
|
||||||
key = String(key.dropFirst())
|
key = String(key.dropFirst())
|
||||||
}
|
}
|
||||||
|
|
||||||
if hex_decode(key) != nil {
|
if hex_decode(key) != nil {
|
||||||
return .hex(key)
|
return .hex(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key.contains { $0 == "@" }) {
|
||||||
|
return .nip05(key)
|
||||||
|
}
|
||||||
|
|
||||||
if let bech_key = decode_bech32_key(key) {
|
if let bech_key = decode_bech32_key(key) {
|
||||||
switch bech_key {
|
switch bech_key {
|
||||||
case .pub(let pk):
|
case .pub(let pk):
|
||||||
@@ -125,49 +184,65 @@ func parse_key(_ thekey: String) -> ParsedKey? {
|
|||||||
return .priv(sec)
|
return .priv(sec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func process_login(_ key: ParsedKey, is_pubkey: Bool) -> Bool {
|
struct NIP05Result: Decodable {
|
||||||
switch key {
|
let names: Dictionary<String, String>
|
||||||
case .priv(let priv):
|
let relays: Dictionary<String, [String]>?
|
||||||
save_privkey(privkey: priv)
|
}
|
||||||
guard let pk = privkey_to_pubkey(privkey: priv) else {
|
|
||||||
return false
|
struct NIP05User {
|
||||||
}
|
let pubkey: String
|
||||||
save_pubkey(pubkey: pk)
|
let relays: [String]
|
||||||
|
}
|
||||||
case .pub(let pub):
|
|
||||||
clear_saved_privkey()
|
func get_nip05_pubkey(id: String) async -> NIP05User? {
|
||||||
save_pubkey(pubkey: pub)
|
let parts = id.components(separatedBy: "@")
|
||||||
|
|
||||||
case .hex(let hexstr):
|
guard parts.count == 2 else {
|
||||||
if is_pubkey {
|
return nil
|
||||||
clear_saved_privkey()
|
}
|
||||||
save_pubkey(pubkey: hexstr)
|
|
||||||
} else {
|
let user = parts[0]
|
||||||
save_privkey(privkey: hexstr)
|
let host = parts[1]
|
||||||
guard let pk = privkey_to_pubkey(privkey: hexstr) else {
|
|
||||||
return false
|
guard let url = URL(string: "https://\(host)/.well-known/nostr.json?name=\(user)") else {
|
||||||
}
|
return nil
|
||||||
save_pubkey(pubkey: pk)
|
}
|
||||||
|
|
||||||
|
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 NIP05User(pubkey: pubkey, relays: relays)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KeyInput: View {
|
struct KeyInput: View {
|
||||||
let title: String
|
let title: String
|
||||||
let key: Binding<String>
|
let key: Binding<String>
|
||||||
|
|
||||||
init(_ title: String, key: Binding<String>) {
|
init(_ title: String, key: Binding<String>) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.key = key
|
self.key = key
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TextField("", text: key)
|
TextField("", text: key)
|
||||||
.placeholder(when: key.wrappedValue.isEmpty) {
|
.placeholder(when: key.wrappedValue.isEmpty) {
|
||||||
|
|||||||
Reference in New Issue
Block a user