damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 53ec89551b4183cd7908b52491bb3915d3f57129
parent 638052492dfb6ac6461463f06c7fe6fb5432b47d
Author: Andrii Sievrikov <devandsev@gmail.com>
Date:   Sat,  4 Feb 2023 23:11:36 -0500

Add local authentication when accessing private key

Changelog-Added: Use local authentication (faceid) to access private key

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 2++
Mdamus/Views/ConfigView.swift | 51++++++++++++++++++++++++++++++++++++++++++++++-----
Mtranslations/en-US.xcloc/Localized Contents/en-US.xliff | 56+++++++++++++++++++++++++++++++++-----------------------
Mtranslations/en-US.xcloc/Source Contents/damus/en-US.lproj/InfoPlist.strings | 2++
Mtranslations/en-US.xcloc/Source Contents/damus/en-US.lproj/Localizable.strings | 0
5 files changed, 83 insertions(+), 28 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -1494,6 +1494,7 @@ INFOPLIST_FILE = damus/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Damus; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; + INFOPLIST_KEY_NSFaceIDUsageDescription = "Local authentication to access private key"; INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -1535,6 +1536,7 @@ INFOPLIST_FILE = damus/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Damus; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; + INFOPLIST_KEY_NSFaceIDUsageDescription = "Local authentication to access private key"; INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift @@ -7,6 +7,7 @@ import AVFoundation import Kingfisher import SwiftUI +import LocalAuthentication struct ConfigView: View { let state: DamusState @@ -14,6 +15,7 @@ struct ConfigView: View { @State var confirm_logout: Bool = false @State var confirm_delete_account: Bool = false @State var show_privkey: Bool = false + @State var has_authenticated_locally: Bool = false @State var show_libretranslate_api_key: Bool = false @State var privkey: String @State var privkey_copied: Bool = false @@ -30,13 +32,45 @@ struct ConfigView: View { _settings = ObservedObject(initialValue: state.settings) } + func authenticateLocally(completion: @escaping (Bool) -> Void) { + // Need to authenticate only once while ConfigView is presented + guard !has_authenticated_locally else { + completion(true) + return + } + let context = LAContext() + if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) { + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: NSLocalizedString("Local authentication to access private key", comment: "Face ID usage description shown when trying to access private key")) { success, error in + DispatchQueue.main.async { + has_authenticated_locally = success + completion(success) + } + } + } else { + // If there's no authentication set up on the device, let the user copy the key without it + has_authenticated_locally = true + completion(true) + } + } + // TODO: (jb55) could be more general but not gonna worry about it atm func CopyButton(is_pk: Bool) -> some View { return Button(action: { - UIPasteboard.general.string = is_pk ? self.state.keypair.pubkey_bech32 : self.privkey - self.privkey_copied = !is_pk - self.pubkey_copied = is_pk - generator.impactOccurred() + let copyKey = { + UIPasteboard.general.string = is_pk ? self.state.keypair.pubkey_bech32 : self.privkey + self.privkey_copied = !is_pk + self.pubkey_copied = is_pk + generator.impactOccurred() + } + if has_authenticated_locally { + copyKey() + } else { + authenticateLocally { success in + if success { + copyKey() + } + } + } }) { let copied = is_pk ? self.pubkey_copied : self.privkey_copied Image(systemName: copied ? "checkmark.circle" : "doc.on.doc") @@ -58,7 +92,7 @@ struct ConfigView: View { if let sec = state.keypair.privkey_bech32 { Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) { HStack { - if show_privkey == false { + if show_privkey == false || !has_authenticated_locally { SecureField(NSLocalizedString("Private Key", comment: "Title of the secure field that holds the user's private key."), text: $privkey) .disabled(true) } else { @@ -70,6 +104,13 @@ struct ConfigView: View { } Toggle(NSLocalizedString("Show", comment: "Toggle to show or hide user's secret account login key."), isOn: $show_privkey) + .onChange(of: show_privkey) { newValue in + if newValue { + authenticateLocally { success in + show_privkey = success + } + } + } } } diff --git a/translations/en-US.xcloc/Localized Contents/en-US.xliff b/translations/en-US.xcloc/Localized Contents/en-US.xliff @@ -15,6 +15,11 @@ <target>damus</target> <note>Bundle name</note> </trans-unit> + <trans-unit id="NSFaceIDUsageDescription" xml:space="preserve"> + <source>Local authentication to access private key</source> + <target>Local authentication to access private key</target> + <note>Privacy - Face ID Usage Description</note> + </trans-unit> <trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve"> <source>Granting Damus access to your photos allows you to save images.</source> <target>Granting Damus access to your photos allows you to save images.</target> @@ -444,6 +449,11 @@ Number of profiles a user is following.</note> <target>Follow</target> <note>Button to follow a user.</note> </trans-unit> + <trans-unit id="Follow me on nostr" xml:space="preserve"> + <source>Follow me on nostr</source> + <target>Follow me on nostr</target> + <note>No comment provided by engineer.</note> + </trans-unit> <trans-unit id="Followers" xml:space="preserve"> <source>Followers</source> <target>Followers</target> @@ -540,6 +550,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>Lightning Invoice</target> <note>Indicates that the view is for paying a Lightning invoice.</note> </trans-unit> + <trans-unit id="Local authentication to access private key" xml:space="preserve"> + <source>Local authentication to access private key</source> + <target>Local authentication to access private key</target> + <note>Face ID usage description shown when trying to access private key</note> + </trans-unit> <trans-unit id="Local default" xml:space="preserve"> <source>Local default</source> <target>Local default</target> @@ -573,11 +588,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>NIP-05 Verification</target> <note>Label for NIP-05 Verification section of user profile form.</note> </trans-unit> - <trans-unit id="No" xml:space="preserve"> - <source>No</source> - <target>No</target> - <note>Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key.</note> - </trans-unit> <trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve"> <source>No block list found, create a new one? This will overwrite any previous block lists.</source> <target>No block list found, create a new one? This will overwrite any previous block lists.</target> @@ -588,11 +598,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>None</target> <note>Dropdown option for selecting no translation server.</note> </trans-unit> - <trans-unit id="Note contains &quot;nsec1&quot; private key. Are you sure?" xml:space="preserve"> - <source>Note contains "nsec1" private key. Are you sure?</source> - <target>Note contains "nsec1" private key. Are you sure?</target> - <note>Alert user that they might be attempting to paste a private key and ask them to confirm.</note> - </trans-unit> <trans-unit id="Nothing to see here. Check back later!" xml:space="preserve"> <source>Nothing to see here. Check back later!</source> <target>Nothing to see here. Check back later!</target> @@ -714,6 +719,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>Relays have been notified and clients will be able to use this information to filter content. Thank you!</target> <note>Description of what was done as a result of sending a report to relay servers.</note> </trans-unit> + <trans-unit id="Remote Image Loading Policy" xml:space="preserve"> + <source>Remote Image Loading Policy</source> + <target>Remote Image Loading Policy</target> + <note>Section title for remote image loading policy</note> + </trans-unit> <trans-unit id="Remove all" xml:space="preserve"> <source>Remove all</source> <target>Remove all</target> @@ -796,6 +806,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>Save Image</target> <note>Context menu option to save an image.</note> </trans-unit> + <trans-unit id="Scan the code" xml:space="preserve"> + <source>Scan the code</source> + <target>Scan the code</target> + <note>No comment provided by engineer.</note> + </trans-unit> <trans-unit id="Search hashtag: #%@" xml:space="preserve"> <source>Search hashtag: #%@</source> <target>Search hashtag: #%@</target> @@ -1011,11 +1026,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>Yes, Overwrite</target> <note>Text of button that confirms to overwrite the existing mutelist.</note> </trans-unit> - <trans-unit id="Yes, Post with Private Key" xml:space="preserve"> - <source>Yes, Post with Private Key</source> - <target>Yes, Post with Private Key</target> - <note>Button to proceed with posting a note even though it looks like they might be posting a private key.</note> - </trans-unit> <trans-unit id="Your Name" xml:space="preserve"> <source>Your Name</source> <target>Your Name</target> @@ -1026,6 +1036,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>Your report will be sent to the relays you are connected to</target> <note>Footer text to inform user what will happen when the report is submitted.</note> </trans-unit> + <trans-unit id="Zaps" xml:space="preserve"> + <source>Zaps</source> + <target>Zaps</target> + <note>Part of a larger sentence to describe how many zap payments there are on a post.</note> + </trans-unit> <trans-unit id="Zebedee" xml:space="preserve"> <source>Zebedee</source> <target>Zebedee</target> @@ -1131,11 +1146,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no <target>you</target> <note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note> </trans-unit> - <trans-unit id="zaps_count" translate="no" xml:space="preserve"> - <source>zaps_count</source> - <target>zaps_count</target> - <note>Part of a larger sentence to describe how many zap payments there are on a post. (Key in .stringsdict)</note> - </trans-unit> <trans-unit id="⚡️ %@" xml:space="preserve"> <source>⚡️ %@</source> <target>⚡️ %@</target> @@ -1281,17 +1291,17 @@ Part of a larger sentence to describe how many profiles a user is following.</no <trans-unit id="/zaps_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <source>%#@ZAPS@</source> <target>%#@ZAPS@</target> - <note>Part of a larger sentence to describe how many zap payments there are on a post.</note> + <note/> </trans-unit> <trans-unit id="/zaps_count:dict/ZAPS:dict/one:dict/:string" xml:space="preserve"> <source>Zap</source> <target>Zap</target> - <note>Part of a larger sentence to describe how many zap payments there are on a post.</note> + <note/> </trans-unit> <trans-unit id="/zaps_count:dict/ZAPS:dict/other:dict/:string" xml:space="preserve"> <source>Zaps</source> <target>Zaps</target> - <note>Part of a larger sentence to describe how many zap payments there are on a post.</note> + <note/> </trans-unit> </body> </file> diff --git a/translations/en-US.xcloc/Source Contents/damus/en-US.lproj/InfoPlist.strings b/translations/en-US.xcloc/Source Contents/damus/en-US.lproj/InfoPlist.strings @@ -2,5 +2,7 @@ "CFBundleDisplayName" = "Damus"; /* Bundle name */ "CFBundleName" = "damus"; +/* Privacy - Face ID Usage Description */ +"NSFaceIDUsageDescription" = "Local authentication to access private key"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Granting Damus access to your photos allows you to save images."; diff --git a/translations/en-US.xcloc/Source Contents/damus/en-US.lproj/Localizable.strings b/translations/en-US.xcloc/Source Contents/damus/en-US.lproj/Localizable.strings Binary files differ.