commit 03931ef70ef9318a347974a4ed6117e0fc83725b
parent 3284832eb05c163cb74072cae19087eff5e35e57
Author: Bryan Montz <bryanmontz@me.com>
Date: Fri, 28 Apr 2023 14:24:34 -0500
Save keys when logging in and when creating new keypair
Changelog-Added: Save keys when logging in and when creating new keypair
Closes: #1042
Diffstat:
4 files changed, 134 insertions(+), 77 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -259,6 +259,7 @@
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50088DA029E8271A008A1FDF /* WebSocket.swift */; };
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
+ 50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; };
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */; };
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FCB2984ACA60072348F /* QRCodeView.swift */; };
@@ -681,6 +682,7 @@
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
50088DA029E8271A008A1FDF /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = "<group>"; };
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
+ 50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
5C42E78B29DB76D90086AAC1 /* EmptyUserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyUserSearchView.swift; sourceTree = "<group>"; };
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
5C513FCB2984ACA60072348F /* QRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeView.swift; sourceTree = "<group>"; };
@@ -1059,6 +1061,7 @@
4C8D00CB29DF92DF0036AF10 /* Hashtags.swift */,
4CDA128B29EB19C40006FA5A /* LocalNotification.swift */,
4CA5588229F33F5B00DC6A45 /* StringCodable.swift */,
+ 50B5685229F97CB400A23243 /* CredentialHandler.swift */,
);
path = Util;
sourceTree = "<group>";
@@ -1761,6 +1764,7 @@
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
4C1A9A1A29DCA17E00516EAC /* ReplyCounter.swift in Sources */,
+ 50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */,
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */,
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */,
4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */,
diff --git a/damus/Util/CredentialHandler.swift b/damus/Util/CredentialHandler.swift
@@ -0,0 +1,48 @@
+//
+// CredentialHandler.swift
+// damus
+//
+// Created by Bryan Montz on 4/26/23.
+//
+
+import Foundation
+import AuthenticationServices
+
+final class CredentialHandler: NSObject, ASAuthorizationControllerDelegate {
+
+ func check_credentials() {
+ let requests: [ASAuthorizationRequest] = [ASAuthorizationPasswordProvider().createRequest()]
+ let authorizationController = ASAuthorizationController(authorizationRequests: requests)
+ authorizationController.delegate = self
+ authorizationController.performRequests()
+ }
+
+ func save_credential(pubkey: String, privkey: String) {
+ SecAddSharedWebCredential("damus.io" as CFString, pubkey as CFString, privkey as CFString, { error in
+ if let error {
+ print("⚠️ An error occurred while saving credentials: \(error)")
+ }
+ })
+ }
+
+ // MARK: - ASAuthorizationControllerDelegate
+ func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
+ guard let cred = authorization.credential as? ASPasswordCredential,
+ let parsedKey = parse_key(cred.password) else {
+ return
+ }
+
+ Task {
+ switch parsedKey {
+ case .pub, .priv:
+ try? await process_login(parsedKey, is_pubkey: false)
+ default:
+ break
+ }
+ }
+ }
+
+ func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
+ print("⚠️ Warning: authentication failed with error: \(error)")
+ }
+}
diff --git a/damus/Views/LoginView.swift b/damus/Views/LoginView.swift
@@ -36,6 +36,7 @@ struct LoginView: View {
@State var key: String = ""
@State var is_pubkey: Bool = false
@State var error: String? = nil
+ @State private var credential_handler = CredentialHandler()
func get_error(parsed_key: ParsedKey?) -> String? {
if self.error != nil {
@@ -43,85 +44,12 @@ struct LoginView: View {
}
if !key.isEmpty && parsed_key == nil {
- return "Invalid key"
+ return LoginError.invalid_key.errorDescription
}
return nil
}
-
- func process_login(_ key: ParsedKey, is_pubkey: Bool) -> Bool {
- switch key {
- case .priv(let priv):
- do {
- try save_privkey(privkey: priv)
- } catch {
- return false
- }
-
- guard let pk = privkey_to_pubkey(privkey: priv) else {
- return false
- }
- save_pubkey(pubkey: pk)
-
- case .pub(let pub):
- do {
- try clear_saved_privkey()
- } catch {
- return false
- }
-
- 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
- }
-
- // this is a weird way to login anyways
- /*
- var bootstrap_relays = load_bootstrap_relays(pubkey: nip05.pubkey)
- 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 {
- do {
- try clear_saved_privkey()
- } catch {
- return false
- }
-
- save_pubkey(pubkey: hexstr)
- } else {
- do {
- try save_privkey(privkey: hexstr)
- } catch {
- return false
- }
-
- 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()
@@ -163,14 +91,21 @@ struct LoginView: View {
if let p = parsed {
DamusWhiteButton(NSLocalizedString("Login", comment: "Button to log into account.")) {
- if !process_login(p, is_pubkey: self.is_pubkey) {
- self.error = NSLocalizedString("Invalid key", comment: "Error message indicating that an invalid account key was entered for login.")
+ Task {
+ do {
+ try await process_login(p, is_pubkey: is_pubkey)
+ } catch {
+ self.error = error.localizedDescription
+ }
}
}
}
}
.padding()
}
+ .onAppear {
+ credential_handler.check_credentials()
+ }
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: BackNav())
}
@@ -214,6 +149,71 @@ func parse_key(_ thekey: String) -> ParsedKey? {
return nil
}
+enum LoginError: LocalizedError {
+ case invalid_key
+ case nip05_failed
+
+ var errorDescription: String? {
+ switch self {
+ case .invalid_key:
+ return NSLocalizedString("Invalid key", comment: "Error message indicating that an invalid account key was entered for login.")
+ case .nip05_failed:
+ return "Could not fetch pubkey"
+ }
+ }
+}
+
+func process_login(_ key: ParsedKey, is_pubkey: Bool) async throws {
+ switch key {
+ case .priv(let priv):
+ try handle_privkey(priv)
+ case .pub(let pub):
+ try clear_saved_privkey()
+ save_pubkey(pubkey: pub)
+
+ case .nip05(let id):
+ guard let nip05 = await get_nip05_pubkey(id: id) else {
+ throw LoginError.nip05_failed
+ }
+
+ // this is a weird way to login anyways
+ /*
+ var bootstrap_relays = load_bootstrap_relays(pubkey: nip05.pubkey)
+ for relay in nip05.relays {
+ if !(bootstrap_relays.contains { $0 == relay }) {
+ bootstrap_relays.append(relay)
+ }
+ }
+ */
+ save_pubkey(pubkey: nip05.pubkey)
+
+ case .hex(let hexstr):
+ if is_pubkey {
+ try clear_saved_privkey()
+ save_pubkey(pubkey: hexstr)
+ } else {
+ try handle_privkey(hexstr)
+ }
+ }
+
+ func handle_privkey(_ privkey: String) throws {
+ try save_privkey(privkey: privkey)
+
+ guard let pk = privkey_to_pubkey(privkey: privkey) else {
+ throw LoginError.invalid_key
+ }
+
+ if let pub = bech32_pubkey(pk), let priv = bech32_privkey(privkey) {
+ CredentialHandler().save_credential(pubkey: pub, privkey: priv)
+ }
+ save_pubkey(pubkey: pk)
+ }
+
+ await MainActor.run {
+ notify(.login, ())
+ }
+}
+
struct NIP05Result: Decodable {
let names: Dictionary<String, String>
let relays: Dictionary<String, [String]>?
diff --git a/damus/Views/SaveKeysView.swift b/damus/Views/SaveKeysView.swift
@@ -6,6 +6,7 @@
//
import SwiftUI
+import Security
struct SaveKeysView: View {
let account: CreateAccountModel
@@ -15,6 +16,8 @@ struct SaveKeysView: View {
@State var priv_copied: Bool = false
@State var loading: Bool = false
@State var error: String? = nil
+
+ @State private var credential_handler = CredentialHandler()
@FocusState var pubkey_focused: Bool
@FocusState var privkey_focused: Bool
@@ -97,6 +100,8 @@ struct SaveKeysView: View {
self.pool.register_handler(sub_id: "signup", handler: handle_event)
+ credential_handler.save_credential(pubkey: account.pubkey_bech32, privkey: account.privkey_bech32)
+
self.loading = true
self.pool.connect()