I have implemented both BGAppRefreshTaskRequest & BGProcessingTaskRequest. I use the AppRefresh for data refresh on the client and small dB updates using RESTful APIs running on an AWS EC2 instance running Django. Users of my iOS app cannot rely on access to the internet when it is in use, so I must queue them to run in background when they are near a WiFi network. The BGAppRefreshTaskRequests to the host are made using NSURLSessionDownloadTasks, as according to Apple Documentation DataTasks are not supported in BGTaskSchedule.
I use ProccessingTask to upload a .jpg file to AWS S3. From the iOS device I execute a HTTP GET NSURLRequest to the host, which returns a dictionary that includes the S3 url, the S3 signature, etc. Per instructions from AWS's web site I create a multipart/form-data NSMutableURLRequest with a boundary string.
I then form a NSURLSessionUploadTask setting the URL and HTTP POST as a multipart/form-data NSMutableURLRequest with a boundary string. I check the response from AWS S3 using the URLSession:session task:didCompleteWithError NSURLSessionTaskDelegate. In analyzing the NSURLResponse it is clear that the system executes a repeat of the NSURLSessionDownloadTask rather than the NSURLSessionUploadTask. There is no response from the AWS S3 host, not even a returned error message.
So, my questions are:
Is NSURLSessionUploadTask allowed with BGTaskSchedule?
If not, should I use NSURLSessionDownloadTask with multipart/form-data to upload a file?
If NSURLSessionUploadTask is allowed, what could be causing the iOS System to repeat a previous NSURLSessionDownloadTask rather than the file upload?
Prior to POSTing the NSURLSessionUploadTask I cancel any remaining NSURLSesstionTasks on the session.
The application is written in Objective C as it uses a C++ library of OpenCV.
Thank you,
Eric
Post
Replies
Boosts
Views
Activity
I am integrating Apple's iOS implementation of WebAuthN using the ASAuthorization APIS provided by Apple. I am integrating with the Django py_webauthn host backend. I have the entire registration & account creations process working. And I almost have the account authentication process working. But there seems to be an issue between the version of authenticationData generated by iOS subsequent to the client (iPhone) authentication process and what py_webauthn is expecting. It results in a consistent
"Leftover bytes detected while parsing authentication data" error
on the host. According to the documentation, this error is generated when all the authenticatorData has been parsed but there are leftover bytes in the data.
I believe if I get past this issue everything will be working.
I could use some help here and provide code/logs below. But in the hopes that someone from the Authentication development team at Apple is watching for these posts, I have a related question.
Can we assume that the generated private key AND sign_count data is shared across an iOS user's devices through KeyChain cloud? Do we have to advise our users that they should enable this??
Here is code (OBJ C) and logs, in the hopes that someone can advise... and hoping that the code can help others trying to implement this capability.
Thanks.....
iPhone Client
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization
//Check to make sure that we are authenticating user after receiving authorization challenge from host
NSLog(@"%s retrieve public/private key",__FUNCTION__);
NSDictionary *pubKeyDict = [NSDictionary dictionary];
ASAuthorizationPlatformPublicKeyCredentialAssertion *appleCred = (ASAuthorizationPlatformPublicKeyCredentialAssertion *)authorization.credential;
NSDictionary *clientJSON = [NSJSONSerialization JSONObjectWithData:appleCred.rawClientDataJSON options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%s clientJSON = %@",__FUNCTION__,clientJSON);
NSString *clientJSONString = [ServerUtilities encodeBase64URLData:appleCred.rawClientDataJSON];
NSString *authenticatorString = [ServerUtilities encodeBase64URLData:appleCred.rawAuthenticatorData];
NSString *signatureString = [ServerUtilities encodeBase64URLData:appleCred.signature];
NSDictionary *respDict = [NSDictionary dictionaryWithObjectsAndKeys clientJSONString, @"clientDataJSON",
authenticatorString, @"signature",
signatureString, @"authenticatorData", nil];
NSLog(@"%s respDict - %@",__FUNCTION__,respDict);
NSString *credIDString = [ServerUtilities encodeBase64URLData:appleCred.credentialID];
NSDictionary *regDict = [NSDictionary dictionaryWithObjectsAndKeys: credIDString, @"id",
respDict, @"response",
@"public-key",@"type", nil];
pubKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: credentialEmail, kEMAIL,
regDict, @"auth_cred", nil];
NSLog(@"%s pubKeyDict - %@",__FUNCTION__,pubKeyDict);
//Post JSON serialized version of pubKeyDict to host
Xcode log
pubKeyDict - {
"auth_cred" = {
id = "JNMBbZF_PQM7O64RY_MXQqhAyKk";
response = {
authenticatorData = "MEYCIQDgVFINihy9nPuuRrZWkLPahEfLy3huCV9_seOuM5gSlgIhAP_99EVQjXhyJy37ccRxQenSxt7oPNrQ4VjVDSW5Ej8l";
clientDataJSON = eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoidkhYcTdKTG05Z2dNU1BienNpZThFWUJsVW16V2N3WXlvOFBnT0FKMlR1b0VBbGVBbkQ0NGFNVDg3RG93YnZaZUNxLWxTdC1uNTFkUWJpVzgxNVhZRlEiLCJvcmlnaW4iOiJodHRwczovL2FuY2hvci1hd2F5LmNvbSJ9;
signature = "WJ3tJNMYfNST99x-EAdNDrnsUOCxqFzvDgXCyTcTg6sdAAAAAA";
};
type = "public-key";
};
email = "my@email.com";
}
Django Python Host
auth_cred_json - {'id': 'JNMBbZF_PQM7O64RY_MXQqhAyKk', 'response': {'clientDataJSON': 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoidkhYcTdKTG05Z2dNU1BienNpZThFWUJsVW16V2N3WXlvOFBnT0FKMlR1b0VBbGVBbkQ0NGFNVDg3RG93YnZaZUNxLWxTdC1uNTFkUWJpVzgxNVhZRlEiLCJvcmlnaW4iOiJodHRwczovL2FuY2hvci1hd2F5LmNvbSJ9', 'signature': 'WJ3tJNMYfNST99x-EAdNDrnsUOCxqFzvDgXCyTcTg6sdAAAAAA', 'authenticatorData': 'MEYCIQDgVFINihy9nPuuRrZWkLPahEfLy3huCV9_seOuM5gSlgIhAP_99EVQjXhyJy37ccRxQenSxt7oPNrQ4VjVDSW5Ej8l'}, 'type': 'public-key', 'rawId': 'JNMBbZF_PQM7O64RY_MXQqhAyKk'}
AuthorizationCredential generated by 'parse_authentication_credential_json'
id='JNMBbZF_PQM7O64RY_MXQqhAyKk'
raw_id=b'$\\xd3\\x01m\\x91\\x7f=\\x03;;\\xae\\x11c\\xf3\\x17B\\xa8@\\xc8\\xa9' response=AuthenticatorAssertionResponse(
client_data_json=b'{
"type":"webauthn.get",
"challenge":"vHXq7JLm9ggMSPbzsie8EYBlUmzWcwYyo8PgOAJ2TuoEAleAnD44aMT87DowbvZeCq-lSt-n51dQbiW815XYFQ",
"origin":"https://anchor-away.com"
}',
authenticator_data=b"0F\\x02!\\x00\\xe0TR\\r\\x8a\\x1c\\xbd\\x9c\\xfb\\xaeF\\xb6V\\x90\\xb3\\xda\\x84G\\xcb\\xcbxn\\t_\\x7f\\xb1\\xe3\\xae3\\x98\\x12\\x96\\x02!\\x00\\xff\\xfd\\xf4EP\\x8dxr'-\\xfbq\\xc4qA\\xe9\\xd2\\xc6\\xde\\xe8<\\xda\\xd0\\xe1X\\xd5\\r%\\xb9\\x12?%",
signature=b'X\\x9d\\xed$\\xd3\\x18|\\xd4\\x93\\xf7\\xdc~\\x10\\x07M\\x0e\\xb9\\xecP\\xe0\\xb1\\xa8\\\\\\xef\\x0e\\x05\\xc2\\xc97\\x13\\x83\\xab\\x1d\\x00\\x00\\x00\\x00',
user_handle=None
)
From a strategy perspective I use BGAppRefreshTask and BGProcessingTasks, successfully implementing them for events triggered within the client application.
But there are certain events that are best triggered from the host, such as when we deploy a new set of vendor or price lists. We are trying to implement this background process using Push Notifications.
We have successfully implemented sending APNS notifications from the host and receive this .json message in.
application: didReceiveRemoteNotification: fetchCompletionHandler:
using this code:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(@"userInfo - %@", userInfo);
switch (application.applicationState)
{
case UIApplicationStateBackground:
{
//Check to see if this is a configs file update background notification
NSArray *dictKeys = [userInfo allKeys];
BOOL foundConfigs = NO;
for (NSString *dk in dictKeys) if ([dk isEqualToString:kCONFIGSVERSION]) foundConfigs = YES;
if (foundConfigs)
{
id configVersion = [userInfo objectForKey:kCONFIGSVERSION];
NSLog(@"AAAppDelegate configVersion - %@", configVersion);
if ([ServerUtilities deviceAssociatedWithAccount]) [self.bkSessionManager retrieveAndInstallUpdatedConfigFilesWithVersion:[configVersion integerValue]];
}
NSLog(@"AAAppDelegate remote notification handler");
completionHandler(UIBackgroundFetchResultNewData);
}
break;
case UIApplicationStateActive:
NSLog(@"AAAppDelegate application is active");
completionHandler(UIBackgroundFetchResultNoData);
break;
case UIApplicationStateInactive:
NSLog(@"AAAppDelegate application is inactive");
completionHandler(UIBackgroundFetchResultNoData);
break;
default:
break;
}
In the Xcode simulator running on an iPhone XS, we place the application in background and then send the APNS message from the host, receiving this .json message in the userInfo dictionary:
userInfo - {
"app_version" = "0.1";
aps = {
"content-available" = 1;
};
"configs_version" = 1;
"dev_os" = 13;
"os_type" = ios;
}
Receipt of this notification then triggers the client to initiate URLSession and retrieve the appropriate files from the host in order to update values on the client. As we are seeing inconsistent behavior we suspect that we are not implementing the URLSession properly.
When setting up the URLSession should it be configured using:
NSURLSessionConfiguration defaultSessionConfiguration
or
NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:
?
This is important to know since using backgroundSession requires us to set up the sessions as download tasks and the defaultSession as data tasks. I have tried various combinations so far without success.
Any leads here will help...