iOS 8 Beta Keychain User Access Control - keychain

I am looking at the new user access control abilities with the introduction of Touch ID when accessing keychain.
Here's the scenario...
I have written a user secret to the keychain with the new access control object in the Keychain query.
Next time I attempt to return this secret, I will be presented with the Touch ID / Passcode interface as expected.
Next time I attempt to write the same data to the keychain, I want to first check if it already exists.
This also presents the Touch ID interface even if i specify the return data attribute to false. But this is not what I want.
Is this the expected behaviour or am I missing a specific attribute to disable the Touch ID interface?

I think what you are trying to achieve is possible by doing something similar to:
// --- Add this code to your save method
// Adds a new keychain item
let status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
if status == errSecSuccess {
println("Keychain Add: \(KeychainResultCode(rawValue: status)?.description)")
return true
} else if status == errSecDuplicateItem {
// perform an update
return self.update(key, data: data)
} else {
return false
}
Does the above works for your needs?

Related

How to manage acces control at login

I been looking for control what a kind of user can see in my app, this is a scholar project. I'm using Swift and Firebase Authentication. I have two kinds of users: Model and Client. In my app I have some views for the Model and other for the Client. What I want to do is that once they log in, in to the app show just the views for their kind of user. I don't know how to verify if the user that is trying to sign in is a Model or a Client.
#IBAction func signInTapped(_ sender: UIButton) {
if validateFields(){
Auth.auth().signIn(withEmail: emailTxt.text!, password: passTxt.text!, completion:{
(user, error) in
if let u = user {
//User is found
}else{
//Error
}
})
}
}
I know that the code need to be where is the comment "User is found" but I don't know if I need to modify something into the the Firebase Console
Create a Firebase Database or Firestore to your project.
Now when you authenticate a user you should also create a userobject in your databse. In this object you can create a field to store whether your user is a model or a client.
Now once the user has signed in, you can download this userobject from the database, check whether the user is a model or client, and send the user to their corresponding views.
You can use custom claims.
You set them using the Admin SDK
// Javascript
admin.auth().setCustomUserClaims(uid, {model: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
Then in client SDK just read the claim.
user.getIDTokenResult(completion: { (result, error) in
guard let model = result?.claims?["model"] as? NSNumber else {
// Something
}
if model.boolValue {
// Show something else
} else {
// Something else again
}
})
Shamelessly copied from Firebase Docs

HealthKit unauthorized versus 0 records in set

IOS 9.2.1, Swift 2.1
I'm trying to give the user a reasonable error message when accessing HealthKit and 0 records are returned for a query.
It could be that there were no records within the selected time range or it could be that the user has disallowed access to that particular dataset inside health. In both case the "storage.requestAuthorizationToShareTypes" provides a "success" value of true.
Is there a way to get the HKHealthKit store give me a code that indicates that the access has been disabled?
My code below
Thanks Mike
import Foundation
import HealthKit
// Interface to the HealthKit
class HealthKitIF {
let storage = HKHealthStore()
var stepsEnabled = false
var bgEnabled = false
var hkSupported = false
init () {
self.checkAuthorization()
}
func checkAuthorization () -> Bool {
// Default to assuming that we're authorized
var isEnabled = true
if (NSClassFromString("HKHealthStore") != nil) { hkSupported = true }
// Do we have access to HealthKit on this device?
if ((hkSupported) && (HKHealthStore.isHealthDataAvailable())) {
// We have to request each data type explicitly
// Ask for BG
var readingsSet = Set<HKObjectType>()
readingsSet.insert(HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)!)
readingsSet.insert(HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!)
storage.requestAuthorizationToShareTypes(nil, readTypes: readingsSet) { (success, error) -> Void in
isEnabled = success
self.bgEnabled = success
}
}
else
{
isEnabled = false
}
return isEnabled
}
Your app should not present an error message when there are no results for a query. HealthKit is designed to keep the user's read authorization choices private by not differentiating between unauthorized access and no data. It may, however, be helpful to include a reminder somewhere in your app or on your support pages that describes how the user can adjust their Health privacy settings if they are not seeing expected behavior in your app.
From the HKHealthStore class reference:
To help prevent possible leaks of sensitive health information, your
app cannot determine whether a user has granted permission to read
data. If you are not given permission, it simply appears as if there
is no data of the requested type in the HealthKit store. If your app
is given share permission but not read permission, you see only the
data that your app has written to the store. Data from other sources
remains hidden.

Set default value for Parse column when new user registers

I am attempting to make a running app, where the user would register and have their own account on the app. But I am using the default Parse signUpViewController when the user is registering. I have a column in the User class in Parse that is marathons completed. Upon registering the value of that column for a new user is undefined, is there a way I can set a default value for when the user registers? Would this be able to be done in the following section?
func signUpViewController(signUpController: PFSignUpViewController, didSignUpUser user: PFUser) {
self.dismissViewControllerAnimated(true, completion: nil)
}
I am using swift, xcode7 along with parse as my backend.
Use a cloud code beforeSave on the User class to assign default values if it's newly created. Here's an example bit of code to check out.
Parse.Cloud.beforeSave(Parse.User, function(request, response) {
// Check if the User is newly created
if (request.object.isNew()) {
// Set default values
request.object.set("marathonsCompleted", 0);
request.object.set("someBooleanColumn", true);
request.object.set("someStringColumn", "someDefaultString");
}
response.success();
});

Swift & Parse.com find by specific column not ID

Reference: http://blog.parse.com/2014/06/06/building-apps-with-parse-and-swift/
I'm trying to find a columns value: userPassword, based in the userName column. Using the above reference from Parse it shows that to get data from parse you should use:
var query = PFQuery(className: "GameScore")
query.getObjectInBackgroundWithId(gameScore.objectId) {
(scoreAgain: PFObject!, error: NSError!) -> Void in
if !error {
NSLog("%#", scoreAgain.objectForKey("playerName") as NSString)
} else {
NSLog("%#", error)
}
}
However, as you can see it is looking for (gameScore.objectId) - The problem is I do not know this value as the user isnt entering a complex parse generated ID. They're entering their chosen username. In the rows I have userName and Password set. How do I search the rows for the userPassword so I can verify it based on their specified userName.
Thanks in advance
Why are you querying the database for a username and password. Adding a new user is very simple with Parse. Taken directly from their docs:
Query User table on Parse
You can query the user table first, using a PFQuery:
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:username];
Adding New User
The idea of user accounts that let people access their information and share it with others in a secure manner is at the core of any social app. Whether your app creates its own sharing environment or integrates with existing social networks, you will need to add functionality to let people manage their accounts in your app.
We provide a specialized user class called PFUser that automatically handles much of the functionality required for user account management.
First make sure to include our SDK libraries from your .h file:
#import <Parse/Parse.h>
Then add this code into your app, for example in the viewDidLoad method (or inside another method that gets called when you run your app):
func myMethod() {
var user = PFUser()
user.username = "myUsername"
user.password = "myPassword"
user.email = "email#example.com"
// other fields can be set just like with PFObject
user["phone"] = "415-392-0202"
user.signUpInBackgroundWithBlock {
(succeeded: Bool!, error: NSError!) -> Void in
if error == nil {
// Hooray! Let them use the app now.
} else {
let errorString = error.userInfo["error"] as NSString
// Show the errorString somewhere and let the user try again.
}
}
}
This call will asynchronously create a new user in your Parse app. Before it does this, it checks to make sure that both the username and email are unique. It also securely hashes the password in the cloud.
You can learn more about Users, including how to verify emails and handle read and write permissions to data, by visiting our docs.
Run your app. A new object of the class User will be sent to the Parse Cloud and saved. When you're ready, click the button below to test if a User was created.
Further
I created a tutorial about connecting to parse if you still wish to go down the route of querying the server manually:
http://ios-blog.co.uk/tutorials/swift-create-user-sign-up-based-app-with-parse-com-using-pfuser/

Why do iOS keychain values not change without leaving the ViewController?

I have an abstraction on the iOS Keychain API that seems to work well. Basically, it has:
public string GetGenericPasswordField(string account)
{
var record = SecKeyChain.QueryAsRecord(query, out code);
if (code == SecStatusCode.ItemNotFound)
return null;
return NSString.FromData(record.ValueData, NSStringEncoding.UTF8);
}
public void SetGenericPasswordField(string account, string value)
{
if (value == null)
{
SecKeyChain.Remove(record);
return;
}
var record = new SecRecord (SecKind.GenericPassword) {
Service = service,
Label = label,
Account = account,
ValueData = NSData.FromString (value),
};
SecStatusCode code = SecKeyChain.Add (record);
if (code == SecStatusCode.DuplicateItem)
{
// (remove and re-add item)
}
}
I have used this abstraction on my app's settings screen to save values while leaving, and then load those values elsewhere in the app.
But I've run into an issue where saving a value does not appear to take effect if you don't leave the current ViewController. What I'm doing is analogous to:
if (Keychain.GetGenericPasswordField("RemoteLogin") == null)
{
var remotePassword = GetFromDialog();
Keychain.SetGenericPasswordField("RemoteLogin", Hash(remotePassword));
// Safe to assume the RemoteLogin password got saved, right?
}
// Later on...
if (Keychain.GetGenericPasswordField("RemoteLogin") == null)
{
// This block is being executed
}
I've stepped through the code in the debugger to confirm that things are as I'm describing them, and that my abstraction method really is getting a SecStatusCode.ItemNotFound back, meaning null is the appropriate value to return.
I worked around this once by moving the first half of my code back to a previous ViewController, and for some reason that seemed to wake it up, or clear out whatever caching is taking place. But now I've encountered another situation where that's not really practical.
Why is this happening? Is my abstraction leaking?