Permalink
Browse files

Store access token and current user record in keychain

  • Loading branch information...
carmenlau committed Oct 2, 2018
2 parents 91bcbd8 + d377443 commit 3ef2b24e65a9b8564bf5f1e319a520130b58c2d5
@@ -29,11 +29,12 @@ @implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application

// You can obtain the following information from portal.skygear.io
[[SKYContainer defaultContainer] configAddress:@"https://example.skygeario.com"];
[[SKYContainer defaultContainer] configureWithAPIKey:@"c4bf6faa7ccb4737b2342d2c319ff6f0"];


SKYConfiguration *config = [[SKYConfiguration alloc] init];
config.endPointAddress = [NSURL URLWithString:@"https://example.skygeario.com"];
config.apiKey = @"c4bf6faa7ccb4737b2342d2c319ff6f0";
[[SKYContainer defaultContainer] configure:config];

// Login with username and password
[[[SKYContainer defaultContainer] auth] loginWithUsername:@"johndoe"
password:@"passw0rd"
@@ -30,7 +30,7 @@ target 'Tests' do
end

target 'Mac Example' do
platform :osx, '10.9'
platform :osx, '10.10'
use_frameworks!

pod "SKYKit/Core", :path => "../"
@@ -31,6 +31,7 @@ PODS:
- SKYKit/Core (1.6.0):
- MagicKit-Skygear (~> 0.0.6)
- SocketRocket (~> 0.4)
- UICKeyChainStore (~> 2.1.0)
- XMLDictionary (~> 1.4.1)
- SKYKit/Core-Swift (1.6.0):
- SKYKit/Core
@@ -43,6 +44,7 @@ PODS:
- SKYKit/Core
- SocketRocket (0.5.1)
- Specta (1.0.7)
- UICKeyChainStore (2.1.1)
- XMLDictionary (1.4.1)

DEPENDENCIES:
@@ -64,6 +66,7 @@ SPEC REPOS:
- OHHTTPStubs
- SocketRocket
- Specta
- UICKeyChainStore
- XMLDictionary

EXTERNAL SOURCES:
@@ -77,11 +80,12 @@ SPEC CHECKSUMS:
MagicKit-Skygear: ca78735168eb8ae24be2307af462e632278bced0
OCMock: 2cd0716969bab32a2283ff3a46fd26a8c8b4c5e3
OHHTTPStubs: 1e21c7d2c084b8153fc53d48400d8919d2d432d0
SKYKit: 2d45c0ddb64e63545765c08b6fcf2279871edd64
SKYKit: dfedf4b4b2394837b135c0fcf0116d587284b474
SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531
Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66
UICKeyChainStore: 239558492fa260531a0774cfc611ea83a6eaff3a
XMLDictionary: fa07b6ff422b3a91d47a5de9bc82e3fc04fbd167

PODFILE CHECKSUM: d2bdbcdda1d05a16a5aa0529882d2a724cbbb7a0
PODFILE CHECKSUM: 24b04b04f7201971a9d306e2ff59c0b7233b4e4b

COCOAPODS: 1.5.3
@@ -83,9 +83,9 @@
A9E9311C1EEA4D18004AF717 /* SKYAuthContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E9311B1EEA4D18004AF717 /* SKYAuthContainerTests.m */; };
A9E9311E1EEA4E2E004AF717 /* SKYPushContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E9311D1EEA4E2E004AF717 /* SKYPushContainerTests.m */; };
A9E931201EEA5406004AF717 /* SKYPubsubContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E9311F1EEA5406004AF717 /* SKYPubsubContainerTests.m */; };
BB8D5796B0ED81845EFF247C /* Pods_Swift_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF830E6671191AF604718509 /* Pods_Swift_Example.framework */; };
CC4B7F2A1FBDB590006D5317 /* OAuthViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC4B7F291FBDB590006D5317 /* OAuthViewController.swift */; };
CC8016141FE2CF90006D3EF7 /* OAuthProfilesResultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC8016131FE2CF90006D3EF7 /* OAuthProfilesResultViewController.swift */; };
CCFA4BF52158AB8000115B64 /* Pods_Swift_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF830E6671191AF604718509 /* Pods_Swift_Example.framework */; };
CD395D0D1AA0273F00A97B16 /* SKYDatabaseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CD395D0C1AA0273F00A97B16 /* SKYDatabaseTest.m */; };
CD42FE0C1AA2DAA20079B3DA /* SKYDeleteRecordsOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CD42FE0B1AA2DAA20079B3DA /* SKYDeleteRecordsOperationTests.m */; };
CD42FE101AA3DB0A0079B3DA /* SKYLogoutUserOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CD42FE0F1AA3DB0A0079B3DA /* SKYLogoutUserOperationTests.m */; };
@@ -253,7 +253,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BB8D5796B0ED81845EFF247C /* Pods_Swift_Example.framework in Frameworks */,
CCFA4BF52158AB8000115B64 /* Pods_Swift_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -784,6 +784,7 @@
"${BUILT_PRODUCTS_DIR}/MagicKit-Skygear-iOS/MagicKit.framework",
"${BUILT_PRODUCTS_DIR}/SKYKit.default-Facebook/SKYKit.framework",
"${BUILT_PRODUCTS_DIR}/SocketRocket-iOS/SocketRocket.framework",
"${BUILT_PRODUCTS_DIR}/UICKeyChainStore-iOS/UICKeyChainStore.framework",
"${BUILT_PRODUCTS_DIR}/XMLDictionary-iOS/XMLDictionary.framework",
"${BUILT_PRODUCTS_DIR}/Expecta/Expecta.framework",
"${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework",
@@ -797,6 +798,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MagicKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SKYKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UICKeyChainStore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/XMLDictionary.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Expecta.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework",
@@ -820,6 +822,7 @@
"${BUILT_PRODUCTS_DIR}/MagicKit-Skygear-iOS/MagicKit.framework",
"${BUILT_PRODUCTS_DIR}/SKYKit.default-Facebook/SKYKit.framework",
"${BUILT_PRODUCTS_DIR}/SocketRocket-iOS/SocketRocket.framework",
"${BUILT_PRODUCTS_DIR}/UICKeyChainStore-iOS/UICKeyChainStore.framework",
"${BUILT_PRODUCTS_DIR}/XMLDictionary-iOS/XMLDictionary.framework",
);
name = "[CP] Embed Pods Frameworks";
@@ -829,6 +832,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MagicKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SKYKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UICKeyChainStore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/XMLDictionary.framework",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -866,6 +870,7 @@
"${BUILT_PRODUCTS_DIR}/MagicKit-Skygear-iOS/MagicKit.framework",
"${BUILT_PRODUCTS_DIR}/SKYKit.default-Facebook/SKYKit.framework",
"${BUILT_PRODUCTS_DIR}/SocketRocket-iOS/SocketRocket.framework",
"${BUILT_PRODUCTS_DIR}/UICKeyChainStore-iOS/UICKeyChainStore.framework",
"${BUILT_PRODUCTS_DIR}/XMLDictionary-iOS/XMLDictionary.framework",
);
name = "[CP] Embed Pods Frameworks";
@@ -875,6 +880,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MagicKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SKYKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UICKeyChainStore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/XMLDictionary.framework",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -892,13 +898,15 @@
"${BUILT_PRODUCTS_DIR}/MagicKit-Skygear-macOS/MagicKit.framework",
"${BUILT_PRODUCTS_DIR}/SKYKit-Core/SKYKit.framework",
"${BUILT_PRODUCTS_DIR}/SocketRocket-macOS/SocketRocket.framework",
"${BUILT_PRODUCTS_DIR}/UICKeyChainStore-macOS/UICKeyChainStore.framework",
"${BUILT_PRODUCTS_DIR}/XMLDictionary-macOS/XMLDictionary.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MagicKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SKYKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UICKeyChainStore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/XMLDictionary.framework",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -27,14 +27,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let endpoint = UserDefaults.standard.string(forKey: "SkygearEndpoint")
if let endpointValue = endpoint {
SKYContainer.default().configAddress(endpointValue)
}
if let endpoint = UserDefaults.standard.string(forKey: "SkygearEndpoint"),
let apiKey = UserDefaults.standard.string(forKey: "SkygearApiKey") {

let apiKey = UserDefaults.standard.string(forKey: "SkygearApiKey")
if let apiKeyValue = apiKey {
SKYContainer.default().configure(withAPIKey: apiKeyValue)
let config = SKYConfiguration()
config.endPointAddress = URL.init(string: endpoint)!
config.apiKey = apiKey
SKYContainer.default().configure(config)
}

application.registerUserNotificationSettings(UIUserNotificationSettings(
@@ -44,6 +44,14 @@ typedef void (^SKYContainerUserOperationActionCompletion)(SKYRecord *_Nullable u
/// Undocumented
@property (nonatomic, readonly) BOOL currentUserVerified;

/**
Set current user data encryption enable state
When this is on, current user data will be saved and retrieved from Keychain instead of
UserDefaults. Default is NO.
*/
- (void)setCurrentUserDataEncryptionEnable:(BOOL)enabled;

/**
Updates the <currentUserRecordID> and <currentAccessToken>. The updated access credentials are also
stored in persistent
@@ -20,6 +20,8 @@
#import "SKYAuthContainer.h"
#import "SKYAuthContainer_Private.h"

#import "UICKeyChainStore.h"

#import "SKYAccessToken.h"
#import "SKYContainer.h"
#import "SKYError.h"
@@ -40,14 +42,28 @@
#import "SKYSetUserDefaultRoleOperation.h"
#import "SKYSignupUserOperation.h"

NSString *const SKYContainerCurrentUserRecordIDKey = @"SKYContainerCurrentUserRecordID";
NSString *const SKYContainerAccessTokenKey = @"SKYContainerAccessToken";
NSString *const SKYContainerCurrentUserRecordKey = @"SKYContainerCurrentUserRecord";

@implementation SKYAuthContainer {
SKYAccessToken *_accessToken;
NSString *_userRecordID;
SKYRecord *_currentUser;
BOOL _currentUserDataEncryptionEnabled;
}

#pragma mark - private

- (instancetype)init
{
self = [super init];
if (self) {
_currentUserDataEncryptionEnabled = NO;
}
return self;
}

- (instancetype)initWithContainer:(SKYContainer *)container
{
self = [super init];
@@ -58,14 +74,48 @@ - (instancetype)initWithContainer:(SKYContainer *)container
}

- (void)loadCurrentUserAndAccessToken
{
if (_currentUserDataEncryptionEnabled) {
[self loadCurrentUserAndAccessTokenFromKeychain];
} else {
[self loadCurrentUserAndAccessTokenFromUserDefaults];
}
}

- (void)loadCurrentUserAndAccessTokenFromKeychain
{
NSString *appBundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:appBundleIdentifier];
keychain.accessibility = UICKeyChainStoreAccessibilityAfterFirstUnlock;

NSString *accessToken = [keychain stringForKey:SKYContainerAccessTokenKey];
NSData *encodedUser = [keychain dataForKey:SKYContainerCurrentUserRecordKey];

SKYRecord *user = nil;
if ([encodedUser isKindOfClass:[NSData class]]) {
user = [NSKeyedUnarchiver unarchiveObjectWithData:encodedUser];
}

if (accessToken && user) {
_currentUser = user;
_userRecordID = user.recordID;
_accessToken = [[SKYAccessToken alloc] initWithTokenString:accessToken];
} else {
_currentUser = nil;
_userRecordID = nil;
_accessToken = nil;
}
}

- (void)loadCurrentUserAndAccessTokenFromUserDefaults
{
NSString *userRecordID =
[[NSUserDefaults standardUserDefaults] objectForKey:@"SKYContainerCurrentUserRecordID"];
[[NSUserDefaults standardUserDefaults] objectForKey:SKYContainerCurrentUserRecordIDKey];
NSString *accessToken =
[[NSUserDefaults standardUserDefaults] objectForKey:@"SKYContainerAccessToken"];
[[NSUserDefaults standardUserDefaults] objectForKey:SKYContainerAccessTokenKey];
SKYRecord *user = nil;
NSData *encodedUser =
[[NSUserDefaults standardUserDefaults] objectForKey:@"SKYContainerCurrentUserRecord"];
[[NSUserDefaults standardUserDefaults] objectForKey:SKYContainerCurrentUserRecordKey];
if ([encodedUser isKindOfClass:[NSData class]]) {
user = [NSKeyedUnarchiver unarchiveObjectWithData:encodedUser];
}
@@ -124,29 +174,58 @@ - (NSString *)currentUserRecordID
}

- (void)saveCurrentUserAndAccessToken
{
if (_currentUserDataEncryptionEnabled) {
[self saveCurrentUserAndAccessTokenToKeychain];
} else {
[self saveCurrentUserAndAccessTokenToUserDefaults];
}
}

- (void)saveCurrentUserAndAccessTokenToKeychain
{
NSString *appBundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:appBundleIdentifier];
keychain.accessibility = UICKeyChainStoreAccessibilityAfterFirstUnlock;

if (_accessToken && _currentUser) {
[keychain setData:[NSKeyedArchiver archivedDataWithRootObject:_currentUser]
forKey:SKYContainerCurrentUserRecordKey];
[keychain setString:_accessToken.tokenString forKey:SKYContainerAccessTokenKey];
} else {
[keychain removeItemForKey:SKYContainerAccessTokenKey];
[keychain removeItemForKey:SKYContainerCurrentUserRecordKey];
}
}

- (void)saveCurrentUserAndAccessTokenToUserDefaults
{
if (_accessToken && (_userRecordID || _currentUser)) {
if (_userRecordID) {
[[NSUserDefaults standardUserDefaults] setObject:_userRecordID
forKey:@"SKYContainerCurrentUserRecordID"];
forKey:SKYContainerCurrentUserRecordIDKey];
}
if (_currentUser) {
[[NSUserDefaults standardUserDefaults]
setObject:[NSKeyedArchiver archivedDataWithRootObject:_currentUser]
forKey:@"SKYContainerCurrentUserRecord"];
forKey:SKYContainerCurrentUserRecordKey];
}
[[NSUserDefaults standardUserDefaults] setObject:_accessToken.tokenString
forKey:@"SKYContainerAccessToken"];
[[NSUserDefaults standardUserDefaults] synchronize];
forKey:SKYContainerAccessTokenKey];
} else {
[[NSUserDefaults standardUserDefaults]
removeObjectForKey:@"SKYContainerCurrentUserRecordID"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"SKYContainerAccessToken"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"SKYContainerCurrentUserRecord"];
removeObjectForKey:SKYContainerCurrentUserRecordIDKey];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:SKYContainerAccessTokenKey];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:SKYContainerCurrentUserRecordKey];
}
[[NSUserDefaults standardUserDefaults] synchronize];
}

- (void)setCurrentUserDataEncryptionEnable:(BOOL)enabled
{
_currentUserDataEncryptionEnabled = enabled;
}

- (void)updateWithUserRecordID:(NSString *)userRecordID accessToken:(SKYAccessToken *)accessToken
{
if (userRecordID && accessToken) {
@@ -0,0 +1,32 @@
//
// SKYConfiguration.h
// SKYKit
//
// Copyright 2015 Oursky Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface SKYConfiguration : NSObject

@property (nonatomic, strong) NSURL *endPointAddress;
@property (nonatomic, strong) NSString *apiKey;
@property (nonatomic, assign) BOOL encryptCurrentUserData;

@end

NS_ASSUME_NONNULL_END
Oops, something went wrong.

0 comments on commit 3ef2b24

Please sign in to comment.