Added keychain storage

Fixes #1
This commit is contained in:
IamTheFij 2018-01-18 13:56:51 -08:00
parent 664af77477
commit 4b3e0898c7
5 changed files with 47 additions and 23 deletions

View File

@ -36,7 +36,6 @@
<rect key="frame" x="16" y="152" width="343" height="495"/> <rect key="frame" x="16" y="152" width="343" height="495"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView> </textView>

View File

@ -9,12 +9,9 @@
import Foundation import Foundation
import Alamofire import Alamofire
import OAuthSwift import OAuthSwift
import Locksmith import SwiftKeychainWrapper
import OAuthSwiftAlamofire import OAuthSwiftAlamofire
let ACCOUNT_NAME = "FitBit"
let SERVICE_NAME = "FitKit"
class FitbitClient { class FitbitClient {
private enum FitKitRequestError: Error { private enum FitKitRequestError: Error {
case unableToParseResults case unableToParseResults
@ -26,6 +23,7 @@ class FitbitClient {
private let callbackUrl = "fitkit://oauth-callback/fitbit" private let callbackUrl = "fitkit://oauth-callback/fitbit"
private let authorizeUrl = "https://www.fitbit.com/oauth2/authorize" private let authorizeUrl = "https://www.fitbit.com/oauth2/authorize"
private let accessTokenUrl = "https://api.fitbit.com/oauth2/token" private let accessTokenUrl = "https://api.fitbit.com/oauth2/token"
private var keychainWrapper: KeychainWrapper
init() { init() {
// Init inner client // Init inner client
@ -41,19 +39,33 @@ class FitbitClient {
let sessionManager = SessionManager.default let sessionManager = SessionManager.default
sessionManager.adapter = self.oauthClient.requestAdapter sessionManager.adapter = self.oauthClient.requestAdapter
sessionManager.retrier = self.oauthClient.requestAdapter sessionManager.retrier = self.oauthClient.requestAdapter
// Use standard KeychainWrapper
self.keychainWrapper = KeychainWrapper.standard
// Attempt to load tokens
self.loadStoredTokens()
} }
func loadStoredTokens() { func loadStoredTokens() {
NSLog("Someday we'll load stored credentials here! Today is not that day") if let credential = self.readCredential() {
/* NSLog("Using credential from Keychain")
let (oAuthData, _) = Locksmith.loadDataForUserAccount(userAccount: ACCOUNT_NAME, inService: SERVICE_NAME) self.oauthClient.client.credential.oauthToken = credential.oauthToken
if let accessToken = oAuthData?.objectForKey("accessToken") { self.oauthClient.client.credential.oauthTokenExpiresAt = credential.oauthTokenExpiresAt
// We have the token!!
self.oauthClient.client.credentials.oauthToken = accessToken
} else {
// No token! Boo
} }
*/ }
func readCredential() -> OAuthSwiftCredential? {
if let credential = self.keychainWrapper.object(forKey: "credential") as? OAuthSwiftCredential {
NSLog("Found credential in keychain")
return credential
}
NSLog("No credential found in keychain")
return nil
}
private func storeCredential(_ credential: OAuthSwiftCredential) -> Bool {
return self.keychainWrapper.set(credential, forKey: "credential")
} }
/// Check if the current client is already authorized /// Check if the current client is already authorized
@ -91,7 +103,17 @@ class FitbitClient {
state:"FITKIT", // TODO: make CSRF token state:"FITKIT", // TODO: make CSRF token
success: { success: {
credential, response, parameters in credential, response, parameters in
NSLog("Succesfully authenticated with credentials %s", credential.oauthToken) // Verify state is consistent
guard parameters["state"] as? String == "FITKIT",
let scope = parameters["scope"] as? String
else {
NSLog("Invalid authorization response")
failure()
return
}
NSLog("Authorized scope: \(scope)")
NSLog("Succesfully authenticated with credentials \(credential.oauthToken)")
let _ = self.storeCredential(credential)
success() success()
}, },
failure: { failure: {
@ -143,13 +165,13 @@ class FitbitClient {
NSLog("Got weight! \(response.result.value!)") NSLog("Got weight! \(response.result.value!)")
guard let results = response.result.value as? [String: Any] else { guard let results = response.result.value as? [String: Any] else {
print("Return result not in form [String: Any]") NSLog("Return result not in form [String: Any]")
callback(nil, FitKitRequestError.unableToParseResults) callback(nil, FitKitRequestError.unableToParseResults)
return return
} }
guard let weights = results["weight"] as? [[String: Any]] else { guard let weights = results["weight"] as? [[String: Any]] else {
print("Return result does not contain a list of weights") NSLog("Return result does not contain a list of weights")
callback(nil, FitKitRequestError.unableToParseResults) callback(nil, FitKitRequestError.unableToParseResults)
return return
} }

View File

@ -45,6 +45,9 @@ class ViewController: UIViewController {
// TODO: Do something with the result and error // TODO: Do something with the result and error
if !self.client.isAuthorized() { if !self.client.isAuthorized() {
self.client.authorize(viewController: self, success: self.authorized, failure: self.unauthroized) self.client.authorize(viewController: self, success: self.authorized, failure: self.unauthroized)
} else {
// TODO: this should be made significantly more clear
self.displayWeight()
} }
} }
} }
@ -65,7 +68,6 @@ class ViewController: UIViewController {
self.client.getWeight(start: lastMonth, end: today) { self.client.getWeight(start: lastMonth, end: today) {
response in FitbitClient.weightResponseHandler(response: response) { response in FitbitClient.weightResponseHandler(response: response) {
fbWeights, error in fbWeights, error in
if let error = error { if let error = error {
NSLog("Unable to get weights: \(error)") NSLog("Unable to get weights: \(error)")
return return
@ -76,6 +78,7 @@ class ViewController: UIViewController {
NSLog("Got Weight: \(fbWeight.logId): \(fbWeight.date) \(fbWeight.weight)") NSLog("Got Weight: \(fbWeight.logId): \(fbWeight.date) \(fbWeight.weight)")
HealthKitHelper.maybeSaveSample(fbWeight, callback: {_,_ in}) HealthKitHelper.maybeSaveSample(fbWeight, callback: {_,_ in})
} }
self.outputText.text = "Health data is synced"
} }
} }
} }

View File

@ -8,7 +8,7 @@ target 'FitKit' do
use_frameworks! use_frameworks!
pod "Alamofire" pod "Alamofire"
pod "Locksmith" pod "SwiftKeychainWrapper"
pod 'OAuthSwift', '~> 1.2.0' pod 'OAuthSwift', '~> 1.2.0'
pod 'OAuthSwiftAlamofire' pod 'OAuthSwiftAlamofire'

View File

@ -1,23 +1,23 @@
PODS: PODS:
- Alamofire (4.5.1) - Alamofire (4.5.1)
- Locksmith (4.0.0)
- OAuthSwift (1.2.0) - OAuthSwift (1.2.0)
- OAuthSwiftAlamofire (0.1.0): - OAuthSwiftAlamofire (0.1.0):
- Alamofire - Alamofire
- OAuthSwift - OAuthSwift
- SwiftKeychainWrapper (3.0.1)
DEPENDENCIES: DEPENDENCIES:
- Alamofire - Alamofire
- Locksmith
- OAuthSwift (~> 1.2.0) - OAuthSwift (~> 1.2.0)
- OAuthSwiftAlamofire - OAuthSwiftAlamofire
- SwiftKeychainWrapper
SPEC CHECKSUMS: SPEC CHECKSUMS:
Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a
Locksmith: e9bebbaaa4cee3c511bc358a44f843c3fe00e21f
OAuthSwift: 7fd6855b8e4d58eb5a30d156ea9bed7a8aecd1ca OAuthSwift: 7fd6855b8e4d58eb5a30d156ea9bed7a8aecd1ca
OAuthSwiftAlamofire: 20bb998ac506f82c3bab0625fbc98a9c6fca783f OAuthSwiftAlamofire: 20bb998ac506f82c3bab0625fbc98a9c6fca783f
SwiftKeychainWrapper: 38952a3636320ae61bad3513cadd870929de7a4a
PODFILE CHECKSUM: 114ede9ace3efbf5222b8790a77f72e973a10d2b PODFILE CHECKSUM: 949ba8ae3b42c5e2974df2d2a6db5effc0cc78a8
COCOAPODS: 1.2.0 COCOAPODS: 1.2.0