diff --git a/CHANGES.rst b/CHANGES.rst index 03ef775929..bfc2d5b065 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,8 +1,13 @@ Changes in Matrix iOS SDK in 0.xx.xx (2019-xx-xx) =============================================== +Improvements: + * MX3PidAddManager: Add User-Interactive Auth to /account/3pid/add (vector-im/riot-ios#2744). + Bug fix: * Room members who left are listed with the actual members (vector-im/riot-ios#2737). + * MX3PidAddManager: Add User-Interactive Auth to /account/3pid/add (vector-im/riot-ios#2744). + * MXHTTPOperation: Make urlResponseFromError return the url response in case of MXError. Changes in Matrix iOS SDK in 0.14.0 (2019-10-11) =============================================== diff --git a/MatrixSDK/Contrib/Swift/MXRestClient.swift b/MatrixSDK/Contrib/Swift/MXRestClient.swift index 0c7fabac27..bbdb6368c3 100644 --- a/MatrixSDK/Contrib/Swift/MXRestClient.swift +++ b/MatrixSDK/Contrib/Swift/MXRestClient.swift @@ -1381,13 +1381,14 @@ public extension MXRestClient { - parameters: - sid: the session id provided during the 3PID validation session. - clientSecret: the same secret key used in the validation session. + - authParameters: The additional authentication information for the user-interactive authentication API. - completion: A block object called when the operation completes. - response: Indicates whether the operation was successful. - returns: a `MXHTTPOperation` instance. */ - @nonobjc @discardableResult func addThirdPartyIdentifierOnly(withSessionId sid: String, clientSecret: String, completion: @escaping (_ response: MXResponse) -> Void) -> MXHTTPOperation { - return __add3PIDOnly(withSessionId: sid, clientSecret: clientSecret, success: currySuccess(completion), failure: curryFailure(completion)) + @nonobjc @discardableResult func addThirdPartyIdentifierOnly(withSessionId sid: String, clientSecret: String, authParameters: [String: Any]?, completion: @escaping (_ response: MXResponse) -> Void) -> MXHTTPOperation { + return __add3PIDOnly(withSessionId: sid, clientSecret: clientSecret, authParams: authParameters, success: currySuccess(completion), failure: curryFailure(completion)) } /** diff --git a/MatrixSDK/Contrib/Swift/ThreePidAdd/MX3PidAddManager.swift b/MatrixSDK/Contrib/Swift/ThreePidAdd/MX3PidAddManager.swift index 89b02a30e5..b50422f643 100644 --- a/MatrixSDK/Contrib/Swift/ThreePidAdd/MX3PidAddManager.swift +++ b/MatrixSDK/Contrib/Swift/ThreePidAdd/MX3PidAddManager.swift @@ -36,6 +36,13 @@ public extension MX3PidAddManager { return __tryFinaliseAddEmailSession(session, success: currySuccess(completion), failure: curryFailure(completion)) } + @nonobjc func tryFinaliseAddEmailSession(_ session: MX3PidAddSession, password: String?, completion: @escaping (_ response: MXResponse) -> Void) -> Void { + return __tryFinaliseAddEmailSession(session, withPassword: password, success: currySuccess(completion), failure: curryFailure(completion)) + } + + @nonobjc func tryFinaliseAddEmailSession(_ session: MX3PidAddSession, authParams: [String: Any]?, completion: @escaping (_ response: MXResponse) -> Void) -> Void { + return __tryFinaliseAddEmailSession(session, authParams: authParams, success: currySuccess(completion), failure: curryFailure(completion)) + } // MARK: - Add MSISDN @nonobjc @discardableResult func startAddPhoneNumberSession(_ phoneNumber: String, countryCode: String?, completion: @escaping (_ response: MXResponse) -> Void) -> MX3PidAddSession { @@ -46,6 +53,14 @@ public extension MX3PidAddManager { return __finaliseAddPhoneNumber(session, withToken: token, success: currySuccess(completion), failure: curryFailure(completion)) } + @nonobjc func finaliseAddPhoneNumberSession(_ session: MX3PidAddSession, token: String, password: String?, completion: @escaping (_ response: MXResponse) -> Void) -> Void { + return __finaliseAddPhoneNumber(session, withToken: token, password: password, success: currySuccess(completion), failure: curryFailure(completion)) + } + + @nonobjc func finaliseAddPhoneNumberSession(_ session: MX3PidAddSession, token: String, authParams: [String: Any]?, completion: @escaping (_ response: MXResponse) -> Void) -> Void { + return __finaliseAddPhoneNumber(session, withToken: token, authParams: authParams, success: currySuccess(completion), failure: curryFailure(completion)) + } + // MARK: - Bind Email @nonobjc @discardableResult func startIdentityServerSession(withEmail email: String, bind: Bool, completion: @escaping (_ response: MXResponse) -> Void) -> MX3PidAddSession { diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index f4d5b0e71f..5454c38ab8 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -1606,6 +1606,7 @@ typedef MXHTTPOperation* (^MXRestClientIdentityServerAccessTokenHandler)(void (^ @param sid the session id provided during the 3PID validation session. @param clientSecret the same secret key used in the validation session. + @param authParameters The additional authentication information for the user-interactive authentication API. @param success A block object called when the operation succeeds. @param failure A block object called when the operation fails. @@ -1614,6 +1615,7 @@ typedef MXHTTPOperation* (^MXRestClientIdentityServerAccessTokenHandler)(void (^ */ - (MXHTTPOperation*)add3PIDOnlyWithSessionId:(NSString*)sid clientSecret:(NSString*)clientSecret + authParams:(NSDictionary*)authParameters success:(void (^)(void))success failure:(void (^)(NSError *error))failure NS_REFINED_FOR_SWIFT; diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index b94358ae48..62924970a1 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -2991,15 +2991,21 @@ - (MXHTTPOperation*)add3PID:(NSString*)sid - (MXHTTPOperation*)add3PIDOnlyWithSessionId:(NSString*)sid clientSecret:(NSString*)clientSecret + authParams:(NSDictionary*)authParameters success:(void (^)(void))success failure:(void (^)(NSError *error))failure { NSString *path = [NSString stringWithFormat:@"%@/account/3pid/add", kMXAPIPrefixPathUnstable]; - NSDictionary *parameters = @{ + NSMutableDictionary *parameters = [@{ @"sid": sid, @"client_secret": clientSecret - }; + } mutableCopy]; + + if (authParameters) + { + parameters[@"auth"] = authParameters; + } MXWeakify(self); return [httpClient requestWithMethod:@"POST" diff --git a/MatrixSDK/ThreePidAdd/MX3PidAddManager.h b/MatrixSDK/ThreePidAdd/MX3PidAddManager.h index cdf333f63c..d9ff1bf740 100644 --- a/MatrixSDK/ThreePidAdd/MX3PidAddManager.h +++ b/MatrixSDK/ThreePidAdd/MX3PidAddManager.h @@ -59,6 +59,20 @@ NS_ERROR_ENUM(MX3PidAddManagerErrorDomain) - (void)cancel3PidAddSession:(MX3PidAddSession*)threePidAddSession NS_REFINED_FOR_SWIFT; +#pragma mark - Add 3rd-Party Identifier + +/** + Get the authentication flow required to add a 3rd party id to the user homeserver account. + + @param success A block object called when the operation succeeds. If the returned flows is nil, no auth is required. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)authenticationFlowForAdd3PidWithSuccess:(void (^)(NSArray * _Nullable flows))success + failure:(void (^)(NSError * _Nonnull))failure; + + #pragma mark - Add Email /** @@ -95,6 +109,15 @@ NS_ERROR_ENUM(MX3PidAddManagerErrorDomain) success:(void (^)(void))success failure:(void (^)(NSError * _Nonnull))failure NS_REFINED_FOR_SWIFT; +- (void)tryFinaliseAddEmailSession:(MX3PidAddSession*)threePidAddSession + withPassword:(nullable NSString*)password + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure NS_REFINED_FOR_SWIFT; + +- (void)tryFinaliseAddEmailSession:(MX3PidAddSession*)threePidAddSession + authParams:(nullable NSDictionary*)authParams + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure NS_REFINED_FOR_SWIFT; #pragma mark - Add MSISDN @@ -131,6 +154,18 @@ NS_ERROR_ENUM(MX3PidAddManagerErrorDomain) success:(void (^)(void))success failure:(void (^)(NSError * _Nonnull))failure NS_REFINED_FOR_SWIFT; +- (void)finaliseAddPhoneNumberSession:(MX3PidAddSession*)threePidAddSession + withToken:(NSString*)token + password:(nullable NSString*)password + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure NS_REFINED_FOR_SWIFT; + +- (void)finaliseAddPhoneNumberSession:(MX3PidAddSession*)threePidAddSession + withToken:(NSString*)token + authParams:(nullable NSDictionary*)authParams + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure NS_REFINED_FOR_SWIFT; + #pragma mark - Bind Email diff --git a/MatrixSDK/ThreePidAdd/MX3PidAddManager.m b/MatrixSDK/ThreePidAdd/MX3PidAddManager.m index b1c18e77c9..231f910c27 100644 --- a/MatrixSDK/ThreePidAdd/MX3PidAddManager.m +++ b/MatrixSDK/ThreePidAdd/MX3PidAddManager.m @@ -51,6 +51,50 @@ - (void)cancel3PidAddSession:(MX3PidAddSession*)threePidAddSession } + +#pragma mark - Add 3rd-Party Identifier + +- (MXHTTPOperation*)authenticationFlowForAdd3PidWithSuccess:(void (^)(NSArray * _Nullable flows))success + failure:(void (^)(NSError * _Nonnull))failure +{ + // Trigger a random request to the API + // If authentication is required, it will provide the flow in the error response + return [self->mxSession.matrixRestClient add3PIDOnlyWithSessionId:@"" clientSecret:@"" authParams:nil success:^{ + // This should not happen + success(nil); + } failure:^(NSError *error) { + NSHTTPURLResponse *urlResponse = [MXHTTPOperation urlResponseFromError:error]; + if (urlResponse) + { + switch (urlResponse.statusCode) + { + case 400: + // No required authentication + success(nil); + break; + + case 401: + { + // Extract authentication flows + MXAuthenticationSession *authSession; + MXJSONModelSetMXJSONModel(authSession, MXAuthenticationSession, error.userInfo[MXHTTPClientErrorResponseDataKey]); + success(authSession.flows); + break; + } + + default: + failure(error); + break; + } + } + else + { + failure(error); + } + }]; +} + + #pragma mark - Add Email - (MX3PidAddSession*)startAddEmailSessionWithEmail:(NSString*)email @@ -94,6 +138,37 @@ - (MX3PidAddSession*)startAddEmailSessionWithEmail:(NSString*)email - (void)tryFinaliseAddEmailSession:(MX3PidAddSession*)threePidAddSession success:(void (^)(void))success failure:(void (^)(NSError * _Nonnull))failure +{ + [self tryFinaliseAddEmailSession:threePidAddSession authParams:nil success:success failure:failure]; +} + +- (void)tryFinaliseAddEmailSession:(MX3PidAddSession*)threePidAddSession + withPassword:(nullable NSString*)password + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure +{ + // Make a first request to start user-interactive authentication + MXWeakify(self); + [self tryFinaliseAddEmailSession:threePidAddSession authParams:nil success:success failure:^(NSError * _Nonnull error) { + MXStrongifyAndReturnIfNil(self); + + NSDictionary *authParams = [self authParamsFromError:error andPassword:password]; + if (authParams) + { + // Retry but authenticated + [self tryFinaliseAddEmailSession:threePidAddSession authParams:authParams success:success failure:failure]; + } + else + { + failure(error); + } + }]; +} + +- (void)tryFinaliseAddEmailSession:(MX3PidAddSession*)threePidAddSession + authParams:(nullable NSDictionary*)authParams + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure { NSLog(@"[MX3PidAddManager] tryFinaliseAddEmailSession: threePid: %@", threePidAddSession); @@ -111,7 +186,7 @@ - (void)tryFinaliseAddEmailSession:(MX3PidAddSession*)threePidAddSession if (doesServerSupportSeparateAddAndBind) { // https://gist.github.com/jryans/839a09bf0c5a70e2f36ed990d50ed928#2b-adding-a-3pid-to-hs-account-after-registration-post-msc2290 - threePidAddSession.httpOperation = [mxSession.matrixRestClient add3PIDOnlyWithSessionId:threePidAddSession.sid clientSecret:threePidAddSession.clientSecret success:^{ + threePidAddSession.httpOperation = [mxSession.matrixRestClient add3PIDOnlyWithSessionId:threePidAddSession.sid clientSecret:threePidAddSession.clientSecret authParams:authParams success:^{ NSLog(@"[MX3PidAddManager] tryFinaliseAddEmailSession: DONE: threePid: %@", threePidAddSession); @@ -187,6 +262,39 @@ - (void)finaliseAddPhoneNumberSession:(MX3PidAddSession*)threePidAddSession withToken:(NSString*)token success:(void (^)(void))success failure:(void (^)(NSError * _Nonnull))failure +{ + [self finaliseAddPhoneNumberSession:threePidAddSession withToken:token authParams:nil success:success failure:failure]; +} + +- (void)finaliseAddPhoneNumberSession:(MX3PidAddSession*)threePidAddSession + withToken:(NSString*)token + password:(nullable NSString*)password + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure +{ + // Make a first request to start user-interactive authentication + MXWeakify(self); + [self finaliseAddPhoneNumberSession:threePidAddSession withToken:token authParams:nil success:success failure:^(NSError * _Nonnull error) { + MXStrongifyAndReturnIfNil(self); + + NSDictionary *authParams = [self authParamsFromError:error andPassword:password]; + if (authParams) + { + // Retry but authenticated + [self finaliseAddPhoneNumberSession:threePidAddSession withToken:token authParams:authParams success:success failure:failure]; + } + else + { + failure(error); + } + }]; +} + +- (void)finaliseAddPhoneNumberSession:(MX3PidAddSession*)threePidAddSession + withToken:(NSString*)token + authParams:(nullable NSDictionary*)authParams + success:(void (^)(void))success + failure:(void (^)(NSError * _Nonnull))failure { NSLog(@"[MX3PidAddManager] finaliseAddPhoneNumberSession: threePid: %@", threePidAddSession); @@ -209,7 +317,7 @@ - (void)finaliseAddPhoneNumberSession:(MX3PidAddSession*)threePidAddSession if (self->doesServerSupportSeparateAddAndBind) { // https://gist.github.com/jryans/839a09bf0c5a70e2f36ed990d50ed928#2b-adding-a-3pid-to-hs-account-after-registration-post-msc2290 - operation = [self->mxSession.matrixRestClient add3PIDOnlyWithSessionId:threePidAddSession.sid clientSecret:threePidAddSession.clientSecret success:^{ + operation = [self->mxSession.matrixRestClient add3PIDOnlyWithSessionId:threePidAddSession.sid clientSecret:threePidAddSession.clientSecret authParams:authParams success:^{ NSLog(@"[MX3PidAddManager] finaliseAddPhoneNumberSession: DONE: threePid: %@", threePidAddSession); @@ -483,6 +591,44 @@ - (MXHTTPOperation *)submitMsisdnTokenOtherUrl:(NSString *)url failure:failure]; } +/** + Build auth params when User-Interactive Authentication is required. + + The supported auth flow is "m.login.password". + + @param error the error got from the API request. + @param password the password to use. + @return the params to make an authenticated API request. + */ +- (NSDictionary*)authParamsFromError:(NSError*)error andPassword:(nullable NSString*)password +{ + NSDictionary *authParams; + NSHTTPURLResponse *urlResponse = [MXHTTPOperation urlResponseFromError:error]; + + if (urlResponse && urlResponse.statusCode == 401) + { + + MXAuthenticationSession *authSession; + MXJSONModelSetMXJSONModel(authSession, MXAuthenticationSession, error.userInfo[MXHTTPClientErrorResponseDataKey]); + if (authSession && password) + { + NSString *userId = self->mxSession.matrixRestClient.credentials.userId; + authParams = @{ + @"type": kMXLoginFlowTypePassword, + @"identifier": @{ + @"type": kMXLoginIdentifierTypeUser, + @"user": userId + }, + @"session": authSession.session, + @"password": password, + @"user": userId + }; + } + } + + return authParams; +} + #pragma mark - Bind to Identity Server - diff --git a/MatrixSDK/Utils/MXHTTPClient.m b/MatrixSDK/Utils/MXHTTPClient.m index 5f47310fc4..c729df3898 100644 --- a/MatrixSDK/Utils/MXHTTPClient.m +++ b/MatrixSDK/Utils/MXHTTPClient.m @@ -38,7 +38,7 @@ */ #define MXHTTPCLIENT_RETRY_JITTER_MS 3000 -NSString * const MXHTTPClientErrorResponseDataKey = @"com.matrixsdk.httpclient.error.response.data"; +NSString* const MXHTTPClientErrorResponseDataKey = @"com.matrixsdk.httpclient.error.response.data"; NSString* const kMXHTTPClientUserConsentNotGivenErrorNotification = @"kMXHTTPClientUserConsentNotGivenErrorNotification"; NSString* const kMXHTTPClientUserConsentNotGivenErrorNotificationConsentURIKey = @"kMXHTTPClientUserConsentNotGivenErrorNotificationConsentURIKey"; NSString* const kMXHTTPClientMatrixErrorNotification = @"kMXHTTPClientMatrixErrorNotification"; diff --git a/MatrixSDK/Utils/MXHTTPOperation.m b/MatrixSDK/Utils/MXHTTPOperation.m index 1df916cc62..56a4f1623d 100644 --- a/MatrixSDK/Utils/MXHTTPOperation.m +++ b/MatrixSDK/Utils/MXHTTPOperation.m @@ -18,6 +18,7 @@ #import "MXHTTPOperation.h" #import +#import "MXError.h" #pragma mark - Constants definitions @@ -88,7 +89,17 @@ - (void)mutateTo:(MXHTTPOperation *)operation + (NSHTTPURLResponse *)urlResponseFromError:(NSError*)error { - return error.userInfo[AFNetworkingOperationFailingURLResponseErrorKey]; + NSHTTPURLResponse *response; + if ([MXError isMXError:error]) + { + MXError *mxError = [[MXError alloc] initWithNSError:error]; + response = mxError.httpResponse; + } + else + { + response = error.userInfo[AFNetworkingOperationFailingURLResponseErrorKey]; + } + return response; } @end