kSecUseAuthenticationUISkip how to use it - ios

I want to add an item to secItemAdd with following access control
SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleAfterFirstUnlock,
/*kecAccessControlTouchIDAny | */kSecAccessControlApplicationPassword, &error);
So it basically asking password for adding item to keychain, that is perfect but when I want to access this Item I want to skip UI authentication and somwhow I wnat to provide the password manually with use of kSecUseAuthenticationUISkip.

As per Apple's documentation on the kSecUseAuthenticationUISkip constant (https://developer.apple.com/reference/security/ksecuseauthenticationuiskip?language=objc)
Silently skip any items that require user authentication. Only use this value with the SecItemCopyMatching function.
So it's not for selectively skipping the authentication, but rather for skipping items that require authentication
Now for your case when you want to secure access to the keychain with an app provided password, when calling SecItemAdd you'd have to use the kSecUseAuthenticationContext key set to a LAContext instance of type LACredentialTypeApplicationPassword that holds the password you ask from your user in conjunction with the kSecAccessControlApplicationPassword in the object you pass to the kSecAttrAccessControl key.
Here'a an article that it explains it with an example that I think it fits your use case pretty well: http://collinbstuart.github.io/lessons/2015/11/04/keychain_entropy

Related

How to make app (with username/password login) know that a user is logged in?

I'm trying to make a login screen and made a database with Firebase by Google. The way I tried to make accounts in database is just by making a new child which is "Users" and then the next child would be a user (which is uniquely defined with a unique username). Then, user has other attributes and an attribute called loggedIn which is set to String 'false' but when on loginScreen login goes successfully set to String 'true'. How can I know after login and when that LoginViewController goes away which account has logged in exactly on that phone (simulator at the time) because there can be more users at one time with the attribute value 'loggedIn' set to 'true' and because of that can't go back to database to check that. I'm really new at this and don't know if this whole approach is okay by making in real-time database something like that and actually checking that by the attribute. Maybe I have to use some kind of a local database or something similar?
Swift 4, iOS development, Xcode 9
If you just want to persist locally the login status you may use UserDefaults. Just have a key called currentUser and set the value as his unique Id.
Once a user logs out just clear that key.
You can also use that to determine whether to launch the login screen when the app opens.
But it may be better to use something like AWS Cognito or similar services to handle user management.
Are you building an iOS app only or a cross-platform project?
I'm not sure this is the perfect answer for you. However, some of my apps use CloudKit and the login is authenticated and only then opens up areas of the app on the user's device. I'm sure if you wanted to check the status of successful logins, the authentication on the cloud could update as confirmation and you could subsequently access the cloud database using the CloudKit Dashboard.
It's quite intuitive and works seamlessly with Xcode.
I have a class called LoginManager, defined as an NSObject. Included in there is an optional String variable called accessToken with setter and getter functions for saving and retrieving my access token, including an option for clearing the token which would be used upon logout. There is also a function to check the status of the key.
var accessToken: String? {
get {
return //key from storage
}
set {
if newValue == nil {
//remove key from storage
} else {
//save key to storage
}
}
}
func isUserLogedIn() -> Bool {
return self.accessToken != nil
}
In my own case, I have an application manager object that changes the root view controller based on this info.
LoginManager.singleton.isUserLogedIn() ? loadMainView() : loadLoginView()
So the resulting view controller is never loaded unless the user is logged in. Is that useful to you?

ADALiOS - how to refresh accessToken silently?

I'm using ADALiOS v3.0.0-pre.2 to connect to Azure AD B2C and authorize a given user. I successfully obtain an accessToken for the user, who gets prompted with UI to login in the process. I use the acquireTokenWithScopes method on ADAuthenticationContext instance to do so.
Somewhere down the line, I want to make sure that the accessToken I obtained earlier is still valid and hasn't been revoked. So, I use acquireTokenSilentWithScopes to check. However, I get an immediate error back saying:
Error raised: 10. Additional Information: Domain: ADAuthenticationErrorDomain Details: The user credentials are need to obtain access token. Please call the non-silent acquireTokenWithResource methods.
What's the right usage of this API such that the token gets silently refreshed or throws an error only when it has been revoked on the server side?
I've managed to beat acquireTokenSilentWithScopes into submission by making the following changes to ADALiOS v3.0.0-pre.2.
Change #1:
ADUserIdentifier has the following class method:
+(BOOL) identifier:(ADUserIdentifier*)identifier matchesInfo:(ADProfileInfo*)info
In it, there are the following lines of code:
NSString* matchString = [identifier userIdMatchString:info];
if (!matchString || [matchString isEqualToString:identifier.userId])
{
return YES;
}
For one reason or another, matchString can sometimes come back as NSNull and calling isEqualToString: method on it will throw. I changed it thusly:
id matchString = [identifier userIdMatchString:info];
if (!matchString || ![matchString isKindOfClass:[NSString class]] || [matchString isEqualToString:identifier.userId])
{
return YES;
}
This seems like a legit bug in the framework that's worth fixing.
Change #2:
When a token is received from AD, ADALiOS tries to store that value in the cache. At some point, it calls ADTokenCacheStoreItem's userCacheKey property, which is defined as follows:
-(NSString*)userCacheKey
{
switch (_identifierType)
{
case OptionalDisplayableId:
case RequiredDisplayableId:
return _profileInfo.username;
case UniqueId:
return _profileInfo.subject;
}
}
In my case, I use RequiredDisplayableId to identify users. In the switch statement above, that translates to _profileInfo.username, which, in turn, returns the preferred_username value from the user profile dictionary. For me that value is not set. So, userCacheKey returns NSNull and the caching mechanism fails.
The values that are set in the user profile dictionary are name and tid. This could be a server misconfiguration, but I worked around the issue by changing the return value of this method to _profileInfo.friendlyName (which maps to name in the user profile dictionary).
Change #3:
The ADKeychainTokenCacheStore, which I use as the concrete ADTokenCacheStoring cache of choice, exposes a sharedGroup property that allows multiple applications to share common keychain secrets. By default, sharedGroup is set to com.microsoft.adalcache. However, since the class is currently private, there is no way to override this value. Also, having that value set requires the iOS app to declare the shared group name in its entitlements. Without these entitlements properly configured, setting values into the keychain fails. So, to work around this issue, I manually set the default sharedGroup value to nil in the ADKeychainTokenCacheStore class itself. I suspect eventually this class will be exposed by the framework as public, but currently that's not the case so I had to hack into it.
Change #4
When I request an auth token from the AD server via the ADALiOS framework, I do so using a policy and a set of scopes. The framework code uses this policy/scope pair to create a lookup key and see if any tokens for that key have already been cached. If none are found, the code contacts the server as expected. Once the server returns an auth token, the framework attempts to cache the value. It constructs a brand new policy/scope key object. However, this time, it uses the policy and scope values that are returned by the server, not the ones I passed in. And, for some reason, the server returns those values to nil. As a result, the new policy/scope key that gets constructed for storage is valid but different from the one I used to look up the cached token initially. So, while the caching operation succeeds, next time I try to look up the auth token using my valid policy/scope pair, the lookup fails.
This may, once again, be a server misconfiguration issue.
Regardless, to fix the problem, I now reset the policy and scope values in the response from the server to the original values I used to generate the server request in the first place. This happens in the following method in ADAuthenticationContext(TokenCaching):
- (void)updateCacheToResult:(ADAuthenticationResult*)result
cacheInstance:(id<ADTokenCacheStoring>)tokenCacheStoreInstance
cacheItem:(ADTokenCacheStoreItem*)cacheItem
withRefreshToken:(NSString*)refreshToken
After all these changes, getting the AD auth token and refreshing it silently seems to work as expected. I'm a little worried about how much I needed to hack into the codebase to make it work. It would be helpful if some MS folks could direct me as to whether these changes were warranted or whether there is a more straight-forward solution.
UPDATE:
It turns out that you don't need to hack into ADKeychainTokenCacheStore directly (change #3 above). The ADAutheticationSettings class exposes a method for you to do so thusly:
[[ADAuthenticationSettings sharedInstance] setSharedCacheKeychainGroup:nil];
I'm Brandon Werner from the Azure Active Directory team. I answered this question here: https://stackoverflow.com/a/44170226/1232116 for the specific question asked.

Anonymously login auto generated user

I see that objectId of users generated locally and users created after anonymous login are not the same.
For example
PFUser.enableAutomaticUser()
let localUser = PFUser.currentUser()!.objectId!
print(localUserId) // "obj1"
PFAnonymousUtils.logInWithBlock {
(user: PFUser?, error: NSError?) -> Void in
let annonUserId: String = PFUser.currentUser()!.objectId!
print(annonUserId) // obj2
}
I want that obj1 to persist throughout the anonymous login phase.
Can I somehow "attach" the locally created user and login him anonymously? or is auto generated users are only useful for when you later upgrade him to a user&pass / social based logins ?
PFAnonymousUtils.logInWithBlock is defined to destroy existing anonymous user data and create a new clean anonymous user. You should only do that when the user is logged out.
If you enable anonymous users then one will be created initially and you can add whatever details you want to that. Then, later, when the user wants a real account use signUp: on the PFUser to convert it.
Note that anonymous users aren't real, you can't use them for everything. So, you may have some issues with them actually participating with other users. If this is the case then you may need to create real placeholder users with auto-generated login details and convert that at a later date by updating the username and sending a forgotten password e-mail (or similar).

Is HKMetadataKeyWasUserEntered broken? I keep getting nil when there is data in the health app

I'm trying to grab data from the Health App. Specifically data that the user did not enter in themselves. For instance, I have an iPhone 6+ that logs the amount of steps that I take. There is also an option to add the data manually; If you add the data manually, the health app marks the data as "user added".
Here's what's confusing me. Let's say I added a step count of 22. When I query the data using HKStatisticsQuery with a predicate of
HKQuery.predicateForObjectsWithMetadataKey(HKMetadataKeyWasUserEntered, allowedValues: [true])
I get the correct result of 22 steps, since I set the allowedValues to true and that I added this myself. However, when I try to set allowedValues to false, I get no results
HKQuery.predicateForObjectsWithMetadataKey(HKMetadataKeyWasUserEntered, allowedValues: [false])
I do indeed have the step data in the health app, but it returned no results.
Check for the below possible areas to fix it:
did you authorized your application to access Steps data from HealthKit?
If your quering for steps count from your application without authorizing with HealthKit, then HealthKit will not return any exception it will simply returns the steps count which was entered from your application only if available, otherwise returns nil.
Before going to query health data, check for the authorization status for steps count using authorizationStatusForType: method available with HKHealthStore class.
Update 1:
My observations on wasUserEntered key is:
If user entering steps data from HEALTH app, respective HKQuantitySample stores metadata dictionary along with HKWasUserEntered key as TRUE automatically.
If user entering steps data from other than Apple's HEALTH app, respective health/fitness device or our application should send metadata dictionary with key HKWasUserEntered along with value as either TRUE/FALSE. Otherwise, the metadata property will contain nil object. Hence, Apple is not applying predicate(predicate contains metadata key) on the data which don't have metadata with it.
For debugging this metadata, try to print your HKQuantitySampleObject.metadata
Apple's implementation on metadata Vs NSPredicate:
If Health data observed from Health/fitness devices, HealthKit is not adding the metadata dictionary to the respective health record.
In case of Health applications other than Apple's Health app, the developer should manually add the metadata dictionary for his record of health data.
If there is no metadata for a specific health record and NSPredicate have a constraint on metadata then, HealthKit completely omitting to validate such records.
Finally,
It is advised to use
(instancetype)quantitySampleWithType:(HKQuantityType *)quantityType
quantity:(HKQuantity *)quantity
startDate:(NSDate *)startDate
endDate:(NSDate *)endDate
metadata:(NSDictionary *)metadata;
instead of
+ (instancetype)quantitySampleWithType:(HKQuantityType *)quantityType
quantity:(HKQuantity *)quantity
startDate:(NSDate *)startDate
endDate:(NSDate *)endDate;
to add metadata.
Reporting Apple regarding this bug that, predicate(which contains metadata key) should be applied on all the data irrespective of checking for metadata exists or not.
It's been a while and you have probably solved this by now, but I think it is worth posting here in case someone else comes across the same issue.
Like Bhanu mentioned, data created by Apple themselves doesn't seem to have the HKWasUserEntered entry as part of its metadata.
But Apple never claims to do it anyway in their docs, so the proper query would be one that filtered out items with HKWasUserEntered == true if that key was set, but also gave back everything else that had no metadata associated.
The ideia is that metadata is a Dictionary, and that type returns nil if a key does not exist. So there are three possible scenarios:
HKMetadataKeyWasUserEntered == nil
HKMetadataKeyWasUserEntered == false
HKMetadataKeyWasUserEntered == true
Out of those, you just want to filter out the last one, since that explicitly tells you that data was entered but the user.
This can be done using the + predicateForObjectsWithMetadataKey:operatorType:value: convenience method on HKQuery, like this:
HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyWasUserEntered, operatorType: .notEqualTo, value: true)
But of course, your initial question was: "Is HKMetadataKeyWasUserEntered broken?". And I don't believe it is, since, like mentioned, Apple never claims to write that key for their own values. :)

Spring Security ACL - create permission

I can use Spring Security ACL with permissions on entity but I'd like to know how to test if a user has access to the "create" (bit 2) permission on a class.
Something like :
aclPermissionEvaluator.hasPermission(auth, clazz, "create")
Could someone help me?
Thanks in advance
You can use Spring's SpEL annotations, e.g. #PreAuthorize, and override the hasPermission method of the PermissionEvaluator interface. If you're using bitwise permission masks, and the user's permissions (as an int) evaluate to '15' (1111), and the required permissions for the object are '6' (0110), you can do something like the following:
public boolean hasPermission(Authentication auth, Object targetObject, Object requiredPermissions) {
int permissionMask = MyUserClass.getMask();
int permissionsRequired = Integer.valueOf(requiredPermissions.toString());
return ((permissionMask | requiredPermissions) == permissionMask);
}
This will return true whenever the bits active in the object's permissions mask are active on the user's permissions. Then, you'd need to declare this custom permission evaluator in your security.xml file:
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator"/>
</bean>
<bean id="permissionEvaluator" class="my.project.package.CustomPermissionEvaluator"/>
Now, whenever you call hasPermission(), your custom evaluator will handle the request. Obviously, you can use whatever logic you like to evaluate permissions -- just make sure the return type is boolean, and the parameters to be passed match what you're sending (or evaluating against; be careful of format exceptions).
Note that your custom parameter must be passed as an Object to override hasPermission(); you could also overload the method by changing the signature to handle whatever parameter type you prefer (e.g. String or int), and the compiler should select the most specific signature. Since you're implementing the PermissionEvaluator interface, however, you'll have to include the given signature (Authentication, Object, Object) anyway, so unless you have some specific need to write an overload method, you may as well just override.
I have exactly the same problem, and sadly, there is no "out of the box" solutions.
One way to do it, if your domain model allow for it, is to add the create permission to the parent object you want to create
For example, imagine you want to create an user for a client. You can add the create permission to the client for the user allowed to create user for this specific client. That the path I choose.
If your domain object doesn't allow for that, the only way I found to do it is:
to create a new table acl_class_entry, wich list acl_entry equivalent but linked to an acl_class and not to an acl_object_identity
Then you create your own permission evaluator, surcharging the method boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) to check the permission against the acl_class_entry if target_id is null. This will allow you to check permission against a class by using the SPel expression hasPermission(nulll, 'className', 'permission')
Of course, you will also need to create your own version of AclService for creating such permission.

Resources