У меня есть развернутое корпоративное приложение, которое использует OpenID Connect
для аутентификации. После аутентификации я сохраняю объект AuthState
в цепочке ключей iOS в целях безопасности и чтобы иметь возможность позже войти в систему с помощью только Face/Touch ID (учитывая, что токен обновления в AuthState все еще действителен). Объект AuthState
выглядит так:
namespace OpenId.AppAuth
{
[Register ("OIDAuthState", true)]
public class AuthState : NSObject, INSCoding, INativeObject, IDisposable, INSSecureCoding
{
[CompilerGenerated]
private static readonly IntPtr class_ptr = Class.GetHandle ("OIDAuthState");
[CompilerGenerated]
private object __mt_ErrorDelegate_var;
[CompilerGenerated]
private object __mt_StateChangeDelegate_var;
public override IntPtr ClassHandle {
get;
}
[CompilerGenerated]
public virtual NSError AuthorizationError {
[Export ("authorizationError")]
get;
}
[CompilerGenerated]
public virtual IAuthStateErrorDelegate ErrorDelegate {
[Export ("errorDelegate", ArgumentSemantic.Weak)]
get;
[Export ("setErrorDelegate:", ArgumentSemantic.Weak)]
set;
}
[CompilerGenerated]
public virtual bool IsAuthorized {
[Export ("isAuthorized")]
get;
}
[CompilerGenerated]
public virtual AuthorizationResponse LastAuthorizationResponse {
[Export ("lastAuthorizationResponse")]
get;
}
[CompilerGenerated]
public virtual RegistrationResponse LastRegistrationResponse {
[Export ("lastRegistrationResponse")]
get;
}
[CompilerGenerated]
public virtual TokenResponse LastTokenResponse {
[Export ("lastTokenResponse")]
get;
}
[CompilerGenerated]
public virtual string RefreshToken {
[Export ("refreshToken")]
get;
}
[CompilerGenerated]
public virtual string Scope {
[Export ("scope")]
get;
}
[CompilerGenerated]
public virtual IAuthStateChangeDelegate StateChangeDelegate {
[Export ("stateChangeDelegate", ArgumentSemantic.Weak)]
get;
[Export ("setStateChangeDelegate:", ArgumentSemantic.Weak)]
set;
}
public static IAuthorizationFlowSession PresentAuthorizationRequest (AuthorizationRequest authorizationRequest, UIViewController presentingViewController, AuthStateAuthorizationCallback callback);
[CompilerGenerated]
[DesignatedInitializer]
[EditorBrowsable (EditorBrowsableState.Advanced)]
[Export ("initWithCoder:")]
public AuthState (NSCoder coder)
: base (NSObjectFlag.Empty);
[CompilerGenerated]
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected AuthState (NSObjectFlag t)
: base (t);
[CompilerGenerated]
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected internal AuthState (IntPtr handle)
: base (handle);
[Export ("initWithAuthorizationResponse:")]
[CompilerGenerated]
public AuthState (AuthorizationResponse authorizationResponse)
: base (NSObjectFlag.Empty);
[Export ("initWithAuthorizationResponse:tokenResponse:")]
[CompilerGenerated]
public AuthState (AuthorizationResponse authorizationResponse, TokenResponse tokenResponse)
: base (NSObjectFlag.Empty);
[Export ("initWithRegistrationResponse:")]
[CompilerGenerated]
public AuthState (RegistrationResponse registrationResponse)
: base (NSObjectFlag.Empty);
[Export ("initWithAuthorizationResponse:tokenResponse:registrationResponse:")]
[DesignatedInitializer]
[CompilerGenerated]
public AuthState (AuthorizationResponse authorizationResponse, TokenResponse tokenResponse, RegistrationResponse registrationResponse)
: base (NSObjectFlag.Empty);
[Export ("encodeWithCoder:")]
[CompilerGenerated]
[Preserve (Conditional = true)]
public virtual void EncodeTo (NSCoder encoder);
[Export ("performActionWithFreshTokens:")]
[CompilerGenerated]
public unsafe virtual void PerformWithFreshTokens ([BlockProxy (typeof(ObjCRuntime.Trampolines.NIDAuthStateAction))] AuthStateAction action);
[Export ("performActionWithFreshTokens:additionalRefreshParameters:")]
[CompilerGenerated]
public unsafe virtual void PerformWithFreshTokens ([BlockProxy (typeof(ObjCRuntime.Trampolines.NIDAuthStateAction))] AuthStateAction action, NSDictionary<NSString, NSString> additionalParameters);
[Export ("authStateByPresentingAuthorizationRequest:UICoordinator:callback:")]
[CompilerGenerated]
public unsafe static IAuthorizationFlowSession PresentAuthorizationRequest (AuthorizationRequest authorizationRequest, IAuthorizationUICoordinator UICoordinator, [BlockProxy (typeof(ObjCRuntime.Trampolines.NIDAuthStateAuthorizationCallback))] AuthStateAuthorizationCallback callback);
[Export ("setNeedsTokenRefresh")]
[CompilerGenerated]
public virtual void SetNeedsTokenRefresh ();
[Export ("tokenRefreshRequest")]
[CompilerGenerated]
public virtual TokenRequest TokenRefreshRequest ();
[Export ("tokenRefreshRequestWithAdditionalParameters:")]
[CompilerGenerated]
public virtual TokenRequest TokenRefreshRequest (NSDictionary<NSString, NSString> additionalParameters);
[Export ("updateWithAuthorizationResponse:error:")]
[CompilerGenerated]
public virtual void Update (AuthorizationResponse authorizationResponse, NSError error);
[Export ("updateWithTokenResponse:error:")]
[CompilerGenerated]
public virtual void Update (TokenResponse tokenResponse, NSError error);
[Export ("updateWithAuthorizationError:")]
[CompilerGenerated]
public virtual void Update (NSError authorizationError);
[Export ("updateWithRegistrationResponse:")]
[CompilerGenerated]
public virtual void UpdateWithRegistrationResponse (RegistrationResponse registrationResponse);
[CompilerGenerated]
protected override void Dispose (bool disposing);
}
}
До iOS 12.1
обновления все работало отлично
ПРОБЛЕМА: после установки обновления ios 12.1 только при работе в режиме выпуска (работает при отладке в режиме разработки при подключении к отладчику VS for Mac) запрашивается существующий ключ в цепочке ключей, хранящей двоичное NSData
представление этот объект AuthState
зависает, и в конечном итоге приложение закрывается из-за того, что оно не отвечает более 10 секунд.
Кто-нибудь сталкивался с подобными проблемами? Было бы удивительно, если бы кто-нибудь мог пролить свет на то, что здесь может происходить, или указать мне правильное направление.
Дополнительная информация:
Как я получаю двоичное представление NSData
объекта AppAuth
:
NSData authStateData = NSKeyedArchiver.ArchivedDataWithRootObject(authState);
Как я сохраняю этот NSData
в связку ключей:
var secAccess = new SecAccessControl(SecAccessible.WhenUnlockedThisDeviceOnly, SecAccessControlCreateFlags.UserPresence);
var secRecord = new SecRecord(SecKind.GenericPassword)
{
Account = "keystring",
Service = "ServiceNameString",
Label = "keystring",
ValueData = authStateData,
AccessControl = secAccess
};
var result = SecKeyChain.Add(secRecord);
Как я запрашиваю существующие данные в keychain
:
var searchRecord = new SecRecord(SecKind.GenericPassword)
{
Service = ServiceName,
Label = key,
};
var match = SecKeyChain.QueryAsRecord(searchRecord, out SecStatusCode resultCode);
Явных ошибок в журнале устройства нет, проверял. Было ли в iOS 12.1 изменение, которое я пропустил и которое существенно повлияло на это?
ОБНОВЛЕНИЕ: я переработал код, чтобы хранить в цепочке ключей только пароль ключа шифрования, а не весь объект AuthState, и вместо этого хранить сериализованный AuthState в зашифрованном виде в локальном файле. Все еще наблюдается та же проблема, в режиме отладки на устройстве все работает, пишет и читает из цепочки для ключей просто отлично, при работе без подключенного отладчика на том же устройстве, та же сборка, пишет нормально, при чтении зависает после успешной проверки TouchID/FaceID, есть ли ошибка в Xamarin.iOS SDK, которая не догнала некоторые изменения в последней версии iOS, вызывающие это?