Skip to content

Commit e02b9bc

Browse files
authored
Merge pull request #4 from rrroyal/feature/keychain-appleid-credentials
Store AppleID credentials in the Keychain
2 parents 4bea83d + 38b1a18 commit e02b9bc

File tree

8 files changed

+279
-134
lines changed

8 files changed

+279
-134
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 & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,69 +3,22 @@
33
<dependencies>
44
<deployment identifier="macosx"/>
55
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
6-
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
76
</dependencies>
87
<scenes>
98
<!--Application-->
109
<scene sceneID="JPo-4y-FX3">
1110
<objects>
1211
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
13-
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="4" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" id="urc-xw-Dhc">
14-
<rect key="frame" x="0.0" y="0.0" width="300" height="46"/>
15-
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
16-
<subviews>
17-
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zLd-d8-ghZ">
18-
<rect key="frame" x="0.0" y="25" width="300" height="21"/>
19-
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Apple ID" drawsBackground="YES" id="BXa-Re-rs3">
20-
<font key="font" metaFont="system"/>
21-
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
22-
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
23-
</textFieldCell>
24-
<connections>
25-
<outlet property="delegate" destination="Voe-Tx-rLC" id="QtW-r2-Vuh"/>
26-
<outlet property="nextKeyView" destination="9rp-Vx-rvB" id="bQY-qj-Sej"/>
27-
</connections>
28-
</textField>
29-
<secureTextField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9rp-Vx-rvB">
30-
<rect key="frame" x="0.0" y="0.0" width="300" height="21"/>
31-
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Password" drawsBackground="YES" usesSingleLineMode="YES" id="xqJ-wt-DlP">
32-
<font key="font" metaFont="system"/>
33-
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
34-
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
35-
<allowedInputSourceLocales>
36-
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
37-
</allowedInputSourceLocales>
38-
</secureTextFieldCell>
39-
<connections>
40-
<outlet property="delegate" destination="Voe-Tx-rLC" id="qav-xj-izy"/>
41-
</connections>
42-
</secureTextField>
43-
</subviews>
44-
<constraints>
45-
<constraint firstItem="9rp-Vx-rvB" firstAttribute="width" secondItem="urc-xw-Dhc" secondAttribute="width" id="Eht-pU-Gyh"/>
46-
<constraint firstItem="zLd-d8-ghZ" firstAttribute="width" secondItem="urc-xw-Dhc" secondAttribute="width" id="mg7-Kq-abL"/>
47-
<constraint firstAttribute="width" constant="300" id="zqf-x6-BET"/>
48-
</constraints>
49-
<visibilityPriorities>
50-
<integer value="1000"/>
51-
<integer value="1000"/>
52-
</visibilityPriorities>
53-
<customSpacing>
54-
<real value="3.4028234663852886e+38"/>
55-
<real value="3.4028234663852886e+38"/>
56-
</customSpacing>
57-
</stackView>
5812
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
5913
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="AltServer" customModuleProvider="target">
6014
<connections>
6115
<outlet property="appMenu" destination="uQy-DD-JDr" id="7cY-Ov-AOW"/>
62-
<outlet property="authenticationAppleIDTextField" destination="zLd-d8-ghZ" id="wW5-0J-zdq"/>
63-
<outlet property="authenticationPasswordTextField" destination="9rp-Vx-rvB" id="ZoC-DI-jzQ"/>
6416
<outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/>
6517
<outlet property="enableJITMenu" destination="la4-Sa-L3C" id="YrW-hR-uA7"/>
6618
<outlet property="installAltStoreMenuItem" destination="MJ8-Lt-SSV" id="KYp-c5-8Ru"/>
6719
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
6820
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
21+
<outlet property="logInMenuItem" destination="k7w-cA-c4B" id="Wta-2g-pGI"/>
6922
<outlet property="sideloadAppMenuItem" destination="x0e-zI-0A2" id="GJo-FY-1GO"/>
7023
<outlet property="sideloadIPAConnectedDevicesMenu" destination="IuI-bV-fTY" id="QQw-St-HfG"/>
7124
</connections>
@@ -129,6 +82,10 @@
12982
<modifierMask key="keyEquivalentModifierMask"/>
13083
</menuItem>
13184
<menuItem isSeparatorItem="YES" id="mVM-Nm-Zi9"/>
85+
<menuItem title="Log in" id="k7w-cA-c4B">
86+
<modifierMask key="keyEquivalentModifierMask"/>
87+
</menuItem>
88+
<menuItem isSeparatorItem="YES" id="mhj-D5-Ycy"/>
13289
<menuItem title="Check for Updates..." id="Tnq-gD-Eic" userLabel="Check for Updates">
13390
<modifierMask key="keyEquivalentModifierMask"/>
13491
<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)