Skip to content

Unarchiving#466

Closed
AndrewMcDrew wants to merge 4 commits into
openid:masterfrom
iqmessenger-bv:unarchiving
Closed

Unarchiving#466
AndrewMcDrew wants to merge 4 commits into
openid:masterfrom
iqmessenger-bv:unarchiving

Conversation

@AndrewMcDrew

Copy link
Copy Markdown

In our project to preserve auth credentials between network sessions we archive the obtained during the successful login OIDAuthState object and store it in the keychain. We used to use +[NSKeyedUnarchiver unarchiveObjectWithData:] to unarchive it back for the next session. But this method is deprecated started from the iOS 12 so we tried to make use of recommended +[NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:] which brought us to the crush in the depth of AppAuth. The thing is about a dictionary encapsulated by OIDServiceDiscovery. Despite confirming to NSSecureCoding it uses -[NSDictionary initWithCoder:]. It's ok and works and tests are green -- only because test's dictionary is a bit artificial. It contains strings only as a values. But in our real deployment it is full of nested collections. An attempt to use -[NSDictionary initWithCoder:] with such a dictionary fails and sets NSKeyedUnarchiver's error property to the appropriate error. So this pr improves the test dictionary with such an example and fixes such a bug trying to maintain the compatibility with the previous versions.

Please note though -- I wasn't able to run the tests on the iOS 10 to check if I broke sth. Obviously it's about AuthenticationServices framework which didn't exist at the moment. Our project is runnable on it too. Looks like this thing is a bit beyond my skills. Here is what XCode greets me with.

2019-10-31 11:43:30.483 xctest[7825:202958] The bundle “AppAuth-iOSTests” couldn’t be loaded because it is damaged or missing necessary resources. Try reinstalling the bundle.
2019-10-31 11:43:30.483 xctest[7825:202958] (dlopen_preflight(/Users/aurban/Library/Developer/Xcode/DerivedData/AppAuth-amlxwizlqbgrzhfccvbvtyvrncxn/Build/Products/Debug-iphonesimulator/AppAuth-iOSTests.xctest/AppAuth-iOSTests): Library not loaded: /System/Library/Frameworks/AuthenticationServices.framework/AuthenticationServices
  Referenced from: /Users/aurban/Library/Developer/Xcode/DerivedData/AppAuth-amlxwizlqbgrzhfccvbvtyvrncxn/Build/Products/Debug-iphonesimulator/AppAuth-iOSTests.xctest/AppAuth-iOSTests
  Reason: no suitable image found.  Did find:
	/System/Library/Frameworks/AuthenticationServices.framework/AuthenticationServices: mach-o, but not built for iOS simulator)
Program ended with exit code: 82

@WilliamDenniss

Copy link
Copy Markdown
Member

Andrew, can you sign the CLA?

@AndrewMcDrew

Copy link
Copy Markdown
Author

But I remember for sure I've signed everything needed during this pr. Should I undergo the procedure with each and every pr?

@WilliamDenniss

Copy link
Copy Markdown
Member

Thanks for referencing the existing CLA, no need to do it again 😅

@julienbodet

Copy link
Copy Markdown
Collaborator

I searched the history of pull requests and I found #315 which was trying to fix the same error. Finally after discussion and investigation the PR was closed (see my comment in related issue 479).

@AndrewMcDrew I suspect you're using unarchivedObjectOfClass:fromData:error: (instead of this deprecated method) to unarchive OIDAuthState and this is what breaks the archive.

Comment thread Source/OIDServiceDiscovery.m Outdated
NSError *error;
NSDictionary *dictionary = [[NSDictionary alloc] initWithCoder:aDecoder];
NSDictionary *dictionary = nil;
if (@available(iOS 11.0, *)) {

@julienbodet julienbodet Nov 25, 2019

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is safe to replace the old encoding/decoding implementation for every versions of iOS. Also this will break backward compatibility for old archives... we need to find way to support unarchiving the old way too.

XCTAssertEqualObjects(discovery.discoveryDictionary, unarchived.discoveryDictionary, @"");

if (@available(iOS 11.0, *)) {
data = [NSKeyedArchiver archivedDataWithRootObject:discovery requiringSecureCoding:YES error:&error];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks the build for older versions of other platforms where the NSKeyedArchiver/NSKeyedUnarchiver are not available.

Need more platform checks:

if (@available(iOS 11, macCatalyst 13, macOS 10.13, tvOS 11, *)) {
  ...
}

@AndrewMcDrew

Copy link
Copy Markdown
Author

I searched the history of pull requests and I found #315 which was trying to fix the same error. Finally after discussion and investigation the PR was closed (see my comment in related issue 479).

Thanks for mentioning that PR. It closes with words

The official samples serialize and deserialize OIDServiceDiscovery objects, and when tested against Google do not show any secure coding exceptions.

So as you can see I am still using the very same sample in the test but inserted an array inside as a real discovery doc would have. The root object of the sample is still a dictionary. But it fails without a fix.

On the other hand -- yes indeed I didn't thought about the data backward compatibility. That's a serious issue, I'm not sure I'm able to handle it now.

@codecov-io

codecov-io commented Nov 25, 2019

Copy link
Copy Markdown

Codecov Report

Merging #466 into master will decrease coverage by 0.01%.
The diff coverage is 85.71%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #466      +/-   ##
==========================================
- Coverage    72.2%   72.19%   -0.02%     
==========================================
  Files          63       63              
  Lines        5400     5423      +23     
==========================================
+ Hits         3899     3915      +16     
- Misses       1501     1508       +7
Impacted Files Coverage Δ
UnitTests/OIDServiceDiscoveryTests.m 85.26% <100%> (+0.2%) ⬆️
Source/OIDServiceDiscovery.m 93.97% <77.77%> (+0.26%) ⬆️
Source/iOS/OIDExternalUserAgentIOS.m 0% <0%> (ø) ⬆️
Source/iOS/OIDExternalUserAgentIOSCustomBrowser.h 0% <0%> (ø) ⬆️
Source/iOS/OIDExternalUserAgentIOSCustomBrowser.m 0% <0%> (ø) ⬆️
Source/iOS/OIDAuthState+IOS.m 0% <0%> (ø) ⬆️
Source/iOS/OIDAuthorizationService+IOS.m 0% <0%> (ø) ⬆️
Source/OIDAuthorizationService.m 53.8% <0%> (+0.37%) ⬆️
Source/OIDAuthState.m 47.01% <0%> (+0.99%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e07ba33...fd9035e. Read the comment docs.

@AndrewMcDrew

Copy link
Copy Markdown
Author

So.

I've dived a bit into how to unarchive the data archived using the previous version of AppAuth. Used the idea expressed here. But it doesn't work. -[NSCoder decodeObjectOfClasses:forKey:] cannot be used in this case because the dictionary wasn't archived with some corresponding key. -[NSDictionary initWithCoder:] cannot be used either because the outer method +[NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:] which starts the whole process leads to the error described in the very first message of this thread.

It looks like it's possible to add some ugly conditional logic out of the AppAuth where unarchiving starts to determine how it was archived and choose the appropriate method there -- either a new one or deprecated in the case we have an old archive. But this leaves the deprecated line in the code so why to bother if it anyway there to stay?

One more thing for anybody with the same problem at hand. Actually we can use our fork because we are just introducing OIDC login into the app so we aren't troubled with the data backward compatibility. But it can lead to the headaches in the future -- if AppAuth changes sth significantly in that area, then we will face some merges into our fork. So we are either going with the risk of Apple removing the old method altogether or AppAuth making some big changes. Decided that AppAuth risk is higher.

So sad but true, not the first attempt in this repo to bring that new methods and get rid of deprecations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants