Skip to content

Commit 3751f7d

Browse files
committed
Save AppleID credentials to Keychain
1 parent a8a6e60 commit 3751f7d

File tree

8 files changed

+271
-78
lines changed

8 files changed

+271
-78
lines changed

AltServer/AltServer.entitlements

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
4-
<dict/>
4+
<dict>
5+
<key>com.apple.security.application-groups</key>
6+
<array>
7+
<string>$(TeamIdentifierPrefix)group.com.rileytestut.AltServer</string>
8+
</array>
9+
<key>keychain-access-groups</key>
10+
<array>
11+
<string>$(AppIdentifierPrefix)group.com.rileytestut.AltServer</string>
12+
</array>
13+
</dict>
514
</plist>

AltServer/AppDelegate.swift

Lines changed: 81 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
3838
@IBOutlet private var installMailPluginMenuItem: NSMenuItem!
3939
@IBOutlet private var installAltStoreMenuItem: NSMenuItem!
4040
@IBOutlet private var sideloadAppMenuItem: NSMenuItem!
41-
42-
private weak var authenticationAppleIDTextField: NSTextField?
43-
private weak var authenticationPasswordTextField: NSSecureTextField?
41+
@IBOutlet private var logInMenuItem: NSMenuItem!
4442

4543
private var connectedDevicesMenuController: MenuController<ALTDevice>!
4644
private var sideloadIPAConnectedDevicesMenuController: MenuController<ALTDevice>!
@@ -119,6 +117,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
119117
self.installMailPlugin()
120118
}
121119
}
120+
121+
setupLoginMenuItem()
122122
}
123123

124124
func applicationWillTerminate(_ aNotification: Notification)
@@ -191,48 +191,32 @@ private extension AppDelegate
191191

192192
func installApplication(at url: URL, to device: ALTDevice)
193193
{
194-
let alert = NSAlert()
195-
alert.messageText = NSLocalizedString("Please enter your Apple ID and password.", comment: "")
196-
alert.informativeText = NSLocalizedString("Your Apple ID and password are not saved and are only sent to Apple for authentication.", comment: "")
197-
198-
let textFieldSize = NSSize(width: 300, height: 22)
199-
200-
let appleIDTextField = NSTextField(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height))
201-
appleIDTextField.delegate = self
202-
appleIDTextField.translatesAutoresizingMaskIntoConstraints = false
203-
appleIDTextField.placeholderString = NSLocalizedString("Apple ID", comment: "")
204-
alert.window.initialFirstResponder = appleIDTextField
205-
self.authenticationAppleIDTextField = appleIDTextField
206-
207-
let passwordTextField = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height))
208-
passwordTextField.delegate = self
209-
passwordTextField.translatesAutoresizingMaskIntoConstraints = false
210-
passwordTextField.placeholderString = NSLocalizedString("Password", comment: "")
211-
self.authenticationPasswordTextField = passwordTextField
212-
213-
appleIDTextField.nextKeyView = passwordTextField
214-
215-
let stackView = NSStackView(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height * 2))
216-
stackView.orientation = .vertical
217-
stackView.distribution = .equalSpacing
218-
stackView.spacing = 0
219-
stackView.addArrangedSubview(appleIDTextField)
220-
stackView.addArrangedSubview(passwordTextField)
221-
alert.accessoryView = stackView
222-
223-
alert.addButton(withTitle: NSLocalizedString("Install", comment: ""))
224-
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
225-
226-
self.authenticationAlert = alert
227-
self.validate()
228-
229-
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
230-
231-
let response = alert.runModal()
232-
guard response == .alertFirstButtonReturn else { return }
233-
234-
let username = appleIDTextField.stringValue
235-
let password = passwordTextField.stringValue
194+
let username: String
195+
let password: String
196+
197+
if let _username = try? Keychain.shared.getValue(for: .appleIDEmail),
198+
let _password = try? Keychain.shared.getValue(for: .appleIDPassword) {
199+
username = _username
200+
password = _password
201+
} else {
202+
let alert = AppleIDAuthenticationAlert()
203+
self.authenticationAlert = alert
204+
205+
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
206+
207+
let didTapContinue = alert.display()
208+
guard didTapContinue else { return }
209+
210+
username = alert.appleIDValue.trimmingCharacters(in: .whitespacesAndNewlines)
211+
password = alert.passwordValue.trimmingCharacters(in: .whitespacesAndNewlines)
212+
213+
do {
214+
try Keychain.shared.setValue(username.isEmpty ? nil : username, for: .appleIDEmail)
215+
try Keychain.shared.setValue(password.isEmpty ? nil : password, for: .appleIDPassword)
216+
} catch {
217+
print("AppleID Auth: Error saving credentials: \(error)")
218+
}
219+
}
236220

237221
func finish(_ result: Result<ALTApplication, Error>)
238222
{
@@ -447,6 +431,58 @@ private extension AppDelegate
447431
}
448432
}
449433

434+
// MARK: - AppDelegate+loginMenuItem
435+
436+
private extension AppDelegate {
437+
private func setupLoginMenuItem() {
438+
do {
439+
let email = try Keychain.shared.getValue(for: .appleIDEmail)
440+
logInMenuItem.title = "Log out (\(email))"
441+
logInMenuItem.action = #selector(logoutFromAppleID)
442+
} catch {
443+
print("Error getting stored AppleID credentials: \(error)")
444+
logInMenuItem.title = "Log in..."
445+
logInMenuItem.action = #selector(loginToAppleID)
446+
}
447+
}
448+
449+
@objc private func loginToAppleID() {
450+
let alert = AppleIDAuthenticationAlert()
451+
452+
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
453+
let didTapContinue = alert.display()
454+
guard didTapContinue else { return }
455+
456+
let username = alert.appleIDValue.trimmingCharacters(in: .whitespacesAndNewlines)
457+
let password = alert.passwordValue.trimmingCharacters(in: .whitespacesAndNewlines)
458+
459+
guard !username.isEmpty && !password.isEmpty else {
460+
print("AppleID Auth: Username and/or password was empty.")
461+
return
462+
}
463+
464+
do {
465+
try Keychain.shared.setValue(username, for: .appleIDEmail)
466+
try Keychain.shared.setValue(password, for: .appleIDPassword)
467+
} catch {
468+
let errorAlert = NSAlert(error: error)
469+
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
470+
errorAlert.runModal()
471+
472+
print("AppleID Auth: Error saving credentials: \(error)")
473+
}
474+
475+
setupLoginMenuItem()
476+
}
477+
478+
@objc private func logoutFromAppleID() {
479+
print("Removing AppleID credentials!")
480+
try? Keychain.shared.setValue(nil, for: .appleIDEmail)
481+
try? Keychain.shared.setValue(nil, for: .appleIDPassword)
482+
setupLoginMenuItem()
483+
}
484+
}
485+
450486
extension AppDelegate: NSMenuDelegate
451487
{
452488
func menuWillOpen(_ menu: NSMenu)
@@ -565,38 +601,6 @@ extension AppDelegate: NSMenuDelegate
565601
}
566602
}
567603

568-
extension AppDelegate: NSTextFieldDelegate
569-
{
570-
func controlTextDidChange(_ obj: Notification)
571-
{
572-
self.validate()
573-
}
574-
575-
func controlTextDidEndEditing(_ obj: Notification)
576-
{
577-
self.validate()
578-
}
579-
580-
private func validate()
581-
{
582-
guard
583-
let appleID = self.authenticationAppleIDTextField?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines),
584-
let password = self.authenticationPasswordTextField?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
585-
else { return }
586-
587-
if appleID.isEmpty || password.isEmpty
588-
{
589-
self.authenticationAlert?.buttons.first?.isEnabled = false
590-
}
591-
else
592-
{
593-
self.authenticationAlert?.buttons.first?.isEnabled = true
594-
}
595-
596-
self.authenticationAlert?.layout()
597-
}
598-
}
599-
600604
extension AppDelegate: UNUserNotificationCenterDelegate
601605
{
602606
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)

AltServer/Base.lproj/Main.storyboard

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<outlet property="installAltStoreMenuItem" destination="MJ8-Lt-SSV" id="KYp-c5-8Ru"/>
6767
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
6868
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
69+
<outlet property="logInMenuItem" destination="k7w-cA-c4B" id="Wta-2g-pGI"/>
6970
<outlet property="sideloadAppMenuItem" destination="x0e-zI-0A2" id="GJo-FY-1GO"/>
7071
<outlet property="sideloadIPAConnectedDevicesMenu" destination="IuI-bV-fTY" id="QQw-St-HfG"/>
7172
</connections>
@@ -129,6 +130,10 @@
129130
<modifierMask key="keyEquivalentModifierMask"/>
130131
</menuItem>
131132
<menuItem isSeparatorItem="YES" id="mVM-Nm-Zi9"/>
133+
<menuItem title="Log in" id="k7w-cA-c4B">
134+
<modifierMask key="keyEquivalentModifierMask"/>
135+
</menuItem>
136+
<menuItem isSeparatorItem="YES" id="mhj-D5-Ycy"/>
132137
<menuItem title="Check for Updates..." id="Tnq-gD-Eic" userLabel="Check for Updates">
133138
<modifierMask key="keyEquivalentModifierMask"/>
134139
<connections>

AltServer/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5+
<key>AppIdentifierPrefix</key>
6+
<string>$(AppIdentifierPrefix)</string>
57
<key>CFBundleDevelopmentRegion</key>
68
<string>$(DEVELOPMENT_LANGUAGE)</string>
79
<key>CFBundleExecutable</key>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// AppleIDAuthenticationAlert.swift
3+
// AltServer
4+
//
5+
// Created by royal on 17/12/2022.
6+
//
7+
8+
import AppKit
9+
10+
// MARK: - AppleIDAuthenticationAlert
11+
12+
final class AppleIDAuthenticationAlert: NSAlert {
13+
14+
private let appleIDTextField: NSTextField
15+
private let passwordTextField: NSSecureTextField
16+
17+
var appleIDValue: String {
18+
get { appleIDTextField.stringValue }
19+
}
20+
21+
var passwordValue: String {
22+
get { passwordTextField.stringValue }
23+
}
24+
25+
override init() {
26+
let textFieldSize = NSSize(width: 300, height: 22)
27+
28+
let stackView = NSStackView(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height * 2))
29+
stackView.orientation = .vertical
30+
stackView.distribution = .equalSpacing
31+
stackView.spacing = 0
32+
33+
appleIDTextField = NSTextField(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height))
34+
appleIDTextField.translatesAutoresizingMaskIntoConstraints = false
35+
appleIDTextField.placeholderString = NSLocalizedString("Apple ID", comment: "")
36+
stackView.addArrangedSubview(appleIDTextField)
37+
38+
passwordTextField = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height))
39+
passwordTextField.translatesAutoresizingMaskIntoConstraints = false
40+
passwordTextField.placeholderString = NSLocalizedString("Password", comment: "")
41+
stackView.addArrangedSubview(passwordTextField)
42+
43+
appleIDTextField.nextKeyView = passwordTextField
44+
45+
super.init()
46+
47+
appleIDTextField.delegate = self
48+
passwordTextField.delegate = self
49+
50+
messageText = NSLocalizedString("Please enter your Apple ID and password.", comment: "")
51+
informativeText = NSLocalizedString("Your Apple ID and password will be saved in the Keychain and will be sent only to Apple servers.", comment: "")
52+
accessoryView = stackView
53+
54+
window.initialFirstResponder = appleIDTextField
55+
56+
addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
57+
addButton(withTitle: NSLocalizedString("Continue", comment: ""))
58+
59+
validateTextFields()
60+
}
61+
62+
/// Displays the alert modally, and returns a `Bool` saying whether the user did press "Continue".
63+
func display() -> Bool {
64+
let result = runModal()
65+
return result == .alertSecondButtonReturn
66+
}
67+
}
68+
69+
// MARK: - AppleIDAuthenticationAlert+NSTextFieldDelegate
70+
71+
extension AppleIDAuthenticationAlert: NSTextFieldDelegate {
72+
func controlTextDidChange(_ obj: Notification) {
73+
validateTextFields()
74+
}
75+
}
76+
77+
// MARK: - AppleIDAuthenticationAlert+Private
78+
79+
private extension AppleIDAuthenticationAlert {
80+
func validateTextFields() {
81+
let isAppleIDTextFieldValid = !appleIDValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
82+
let isPasswordTextFieldValid = !passwordValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
83+
buttons.last?.isEnabled = isAppleIDTextFieldValid && isPasswordTextFieldValid
84+
}
85+
}

0 commit comments

Comments
 (0)