Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions assets/lang/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,14 @@ const translations = {
action: 'Export backup key',
},
},
ShareExtension: {
title: 'Save to Internxt',
notSignedIn: {
title: "You're not signed in",
subtitle: 'Log in to upload files securely',
openLogin: 'Open Internxt login',
},
},
},
buttons: {
fixPhoto: 'Repair photo',
Expand Down Expand Up @@ -1126,6 +1134,14 @@ const translations = {
action: 'Exportar la clave de seguridad',
},
},
ShareExtension: {
title: 'Guardar en Internxt',
notSignedIn: {
title: 'No has iniciado sesión',
subtitle: 'Inicia sesión para subir archivos de forma segura',
openLogin: 'Abrir login de Internxt',
},
},
},
buttons: {
fixPhoto: 'Reparar foto',
Expand Down
28 changes: 14 additions & 14 deletions ios/Internxt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,13 @@
LastUpgradeCheck = 1130;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = JR4S3SY396;
LastSwiftMigration = 1250;
DevelopmentTeam = "JR4S3SY396";
ProvisioningStyle = Automatic;
};
573EC8782D084646801534FA = {
DevelopmentTeam = JR4S3SY396;
LastSwiftMigration = 1250;
DevelopmentTeam = "JR4S3SY396";
ProvisioningStyle = Automatic;
};
};
Expand Down Expand Up @@ -607,7 +607,10 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = InternxtShareExtension/InternxtShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JR4S3SY396;
ENABLE_ON_DEMAND_RESOURCES = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = InternxtShareExtension/Info.plist;
Expand All @@ -624,9 +627,6 @@
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
DEVELOPMENT_TEAM = "JR4S3SY396";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
};
name = Debug;
};
Expand All @@ -637,7 +637,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Internxt/Internxt.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JR4S3SY396;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
Expand All @@ -663,9 +666,6 @@
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
DEVELOPMENT_TEAM = "JR4S3SY396";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
};
name = Debug;
};
Expand All @@ -676,7 +676,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Internxt/Internxt.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JR4S3SY396;
INFOPLIST_FILE = Internxt/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = (
Expand All @@ -696,9 +699,6 @@
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
DEVELOPMENT_TEAM = "JR4S3SY396";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
};
name = Release;
};
Expand Down Expand Up @@ -826,7 +826,10 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = InternxtShareExtension/InternxtShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JR4S3SY396;
ENABLE_ON_DEMAND_RESOURCES = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = InternxtShareExtension/Info.plist;
Expand All @@ -842,9 +845,6 @@
PRODUCT_NAME = InternxtShareExtension;
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
DEVELOPMENT_TEAM = "JR4S3SY396";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
};
name = Release;
};
Expand Down
92 changes: 92 additions & 0 deletions ios/Internxt/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Expo
import React
import ReactAppDependencyProvider
import Security

@UIApplicationMain
public class AppDelegate: ExpoAppDelegate {
Expand Down Expand Up @@ -29,9 +30,100 @@ public class AppDelegate: ExpoAppDelegate {
launchOptions: launchOptions)
#endif

syncAuthStatusToAppGroup()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

// Sync auth status whenever the app moves to background so the share
// extension always reads an up-to-date value from the shared UserDefaults.
public override func applicationDidEnterBackground(_ application: UIApplication) {
syncAuthStatusToAppGroup()
super.applicationDidEnterBackground(application)
}

// MARK: - App Group auth sync

private func syncAuthStatusToAppGroup() {
guard let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String,
let defaults = UserDefaults(suiteName: appGroup),
let sharedGroup = Bundle.main.object(forInfoDictionaryKey: "SharedKeychainGroup") as? String
else { return }

let isAuthenticated = privateKeychainItemExists(key: "photosToken")
defaults.set(isAuthenticated, forKey: "isAuthenticated")

if isAuthenticated {
copyToSharedKeychain(privateKey: "photosToken", sharedKey: "shared_photosToken", accessGroup: sharedGroup)
copyToSharedKeychain(privateKey: "xUser_mnemonic", sharedKey: "shared_mnemonic", accessGroup: sharedGroup)
defaults.set(readEmailFromKeychain(), forKey: "userEmail")
} else {
deleteFromSharedKeychain(key: "shared_photosToken", accessGroup: sharedGroup)
deleteFromSharedKeychain(key: "shared_mnemonic", accessGroup: sharedGroup)
defaults.removeObject(forKey: "userEmail")
}
}

private func readEmailFromKeychain() -> String? {
guard let data = readFromPrivateKeychain(key: "xUser_data"),
var raw = String(data: data, encoding: .utf8) else { return nil }
if raw.hasPrefix("\"") && raw.hasSuffix("\"") {
raw = String(raw.dropFirst().dropLast())
}
guard let jsonData = raw.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
let email = json["email"] as? String else { return nil }
return email
}

private func privateKeychainItemExists(key: String) -> Bool {
return readFromPrivateKeychain(key: key) != nil
}

private func copyToSharedKeychain(privateKey: String, sharedKey: String, accessGroup: String) {
guard let data = readFromPrivateKeychain(key: privateKey) else { return }
writeToSharedKeychain(data: data, key: sharedKey, accessGroup: accessGroup)
}

private func readFromPrivateKeychain(key: String) -> Data? {
var result: AnyObject?
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "app:no-auth",
kSecAttrGeneric as String: Data(key.utf8),
kSecAttrAccount as String: Data(key.utf8),
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true,
]
guard SecItemCopyMatching(query as CFDictionary, &result) == errSecSuccess,
let data = result as? Data else { return nil }
return data
}

private func writeToSharedKeychain(data: Data, key: String, accessGroup: String) {
deleteFromSharedKeychain(key: key, accessGroup: accessGroup)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "app:no-auth",
kSecAttrGeneric as String: Data(key.utf8),
kSecAttrAccount as String: Data(key.utf8),
kSecAttrAccessGroup as String: accessGroup,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked,
kSecValueData as String: data,
]
SecItemAdd(query as CFDictionary, nil)
}

private func deleteFromSharedKeychain(key: String, accessGroup: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "app:no-auth",
kSecAttrGeneric as String: Data(key.utf8),
kSecAttrAccount as String: Data(key.utf8),
kSecAttrAccessGroup as String: accessGroup,
]
SecItemDelete(query as CFDictionary)
}

// Linking API
public override func application(
_ app: UIApplication,
Expand Down
2 changes: 2 additions & 0 deletions ios/Internxt/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<string>group.com.internxt.snacks</string>
<key>AppGroupIdentifier</key>
<string>group.com.internxt.snacks</string>
<key>SharedKeychainGroup</key>
<string>$(AppIdentifierPrefix)group.com.internxt.snacks</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
Expand Down
4 changes: 4 additions & 0 deletions ios/Internxt/Internxt.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@
<array>
<string>group.com.internxt.snacks</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)group.com.internxt.snacks</string>
</array>
</dict>
</plist>
2 changes: 2 additions & 0 deletions ios/InternxtShareExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<string>group.com.internxt.snacks</string>
<key>AppGroupIdentifier</key>
<string>group.com.internxt.snacks</string>
<key>SharedKeychainGroup</key>
<string>$(AppIdentifierPrefix)group.com.internxt.snacks</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@
<array>
<string>group.com.internxt.snacks</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)group.com.internxt.snacks</string>
</array>
</dict>
</plist>
Loading
Loading