1879 lines
82 KiB
Objective-C
1879 lines
82 KiB
Objective-C
/*
|
|
Copyright 2019-2024 New Vector Ltd.
|
|
Copyright 2017 Vector Creations Ltd
|
|
Copyright 2016 OpenMarket Ltd
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
Please see LICENSE in the repository root for full details.
|
|
*/
|
|
|
|
#import "AuthInputsView.h"
|
|
|
|
#import "ThemeService.h"
|
|
#import "Tools.h"
|
|
|
|
#import "CountryPickerViewController.h"
|
|
#import "NBPhoneNumberUtil.h"
|
|
|
|
#import "RiotNavigationController.h"
|
|
|
|
#import "GeneratedInterface-Swift.h"
|
|
|
|
@interface AuthInputsView () <MXKCountryPickerViewControllerDelegate>
|
|
{
|
|
/**
|
|
The current email validation
|
|
*/
|
|
MXK3PID *submittedEmail;
|
|
|
|
/**
|
|
The current msisdn validation
|
|
*/
|
|
MXK3PID *submittedMSISDN;
|
|
UINavigationController *phoneNumberPickerNavigationController;
|
|
CountryPickerViewController *phoneNumberCountryPicker;
|
|
NBPhoneNumber *nbPhoneNumber;
|
|
|
|
/**
|
|
The set of parameters ready to use for a registration.
|
|
*/
|
|
NSDictionary *externalRegistrationParameters;
|
|
}
|
|
|
|
/**
|
|
The current view container displayed at last position.
|
|
*/
|
|
@property (nonatomic) UIView *currentLastContainer;
|
|
|
|
@end
|
|
|
|
@implementation AuthInputsView
|
|
@synthesize softLogoutCredentials;
|
|
|
|
+ (UINib *)nib
|
|
{
|
|
return [UINib nibWithNibName:NSStringFromClass(self)
|
|
bundle:[NSBundle bundleForClass:self]];
|
|
}
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
[super awakeFromNib];
|
|
|
|
_thirdPartyIdentifiersHidden = YES;
|
|
_isThirdPartyIdentifierPending = NO;
|
|
_isSingleSignOnRequired = NO;
|
|
|
|
self.userLoginTextField.placeholder = [VectorL10n authUserIdPlaceholder];
|
|
self.repeatPasswordTextField.placeholder = [VectorL10n authRepeatPasswordPlaceholder];
|
|
self.passWordTextField.placeholder = [VectorL10n authPasswordPlaceholder];
|
|
|
|
// Apply placeholder color
|
|
[self customizeViewRendering];
|
|
}
|
|
|
|
- (void)destroy
|
|
{
|
|
[super destroy];
|
|
|
|
submittedEmail = nil;
|
|
submittedMSISDN = nil;
|
|
}
|
|
|
|
-(void)layoutSubviews
|
|
{
|
|
[super layoutSubviews];
|
|
|
|
if (_currentLastContainer)
|
|
{
|
|
self.currentLastContainer = _currentLastContainer;
|
|
}
|
|
}
|
|
|
|
#pragma mark - Override MXKView
|
|
|
|
-(void)customizeViewRendering
|
|
{
|
|
[super customizeViewRendering];
|
|
|
|
self.repeatPasswordTextField.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
self.userLoginTextField.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
self.passWordTextField.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
|
|
self.emailTextField.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
self.phoneTextField.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
|
|
self.isoCountryCodeLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
self.callingCodeLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
|
|
self.countryCodeButton.tintColor = ThemeService.shared.theme.textSecondaryColor;
|
|
|
|
self.messageLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
|
|
self.messageLabel.numberOfLines = 0;
|
|
|
|
self.userLoginSeparator.backgroundColor = ThemeService.shared.theme.lineBreakColor;
|
|
self.emailSeparator.backgroundColor = ThemeService.shared.theme.lineBreakColor;
|
|
self.phoneSeparator.backgroundColor = ThemeService.shared.theme.lineBreakColor;
|
|
self.passwordSeparator.backgroundColor = ThemeService.shared.theme.lineBreakColor;
|
|
self.repeatPasswordSeparator.backgroundColor = ThemeService.shared.theme.lineBreakColor;
|
|
|
|
[self.ssoButton.layer setCornerRadius:5];
|
|
self.ssoButton.clipsToBounds = YES;
|
|
[self.ssoButton setTitle:[VectorL10n authLoginSingleSignOn] forState:UIControlStateNormal];
|
|
[self.ssoButton setTitle:[VectorL10n authLoginSingleSignOn] forState:UIControlStateHighlighted];
|
|
self.ssoButton.backgroundColor = ThemeService.shared.theme.tintColor;
|
|
|
|
self.recaptchaContainer.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
|
|
|
if (self.userLoginTextField.placeholder)
|
|
{
|
|
self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.userLoginTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
}
|
|
|
|
if (self.repeatPasswordTextField.placeholder)
|
|
{
|
|
self.repeatPasswordTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.repeatPasswordTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
|
|
}
|
|
|
|
if (self.passWordTextField.placeholder)
|
|
{
|
|
self.passWordTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.passWordTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
}
|
|
|
|
if (self.phoneTextField.placeholder)
|
|
{
|
|
self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.phoneTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
}
|
|
|
|
if (self.emailTextField.placeholder)
|
|
{
|
|
self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.emailTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
|
|
{
|
|
if (type == MXKAuthenticationTypeLogin || type == MXKAuthenticationTypeRegister)
|
|
{
|
|
// Validate first the provided session
|
|
MXAuthenticationSession *validSession = [self validateAuthenticationSession:authSession];
|
|
|
|
// Cancel email validation if any
|
|
if (submittedEmail)
|
|
{
|
|
[submittedEmail cancelCurrentRequest];
|
|
submittedEmail = nil;
|
|
}
|
|
|
|
// Cancel msisdn validation if any
|
|
if (submittedMSISDN)
|
|
{
|
|
[submittedMSISDN cancelCurrentRequest];
|
|
submittedMSISDN = nil;
|
|
}
|
|
|
|
// Reset external registration parameters
|
|
externalRegistrationParameters = nil;
|
|
|
|
// Reset UI by hidding all items
|
|
[self hideInputsContainer];
|
|
|
|
if ([super setAuthSession:validSession withAuthType:authType])
|
|
{
|
|
if (authType == MXKAuthenticationTypeLogin)
|
|
{
|
|
_isSingleSignOnRequired = NO;
|
|
|
|
if ([self isFlowSupported:kMXLoginFlowTypePassword])
|
|
{
|
|
BOOL showPhoneTextField = BuildSettings.authScreenShowPhoneNumber;
|
|
|
|
self.passWordTextField.returnKeyType = UIReturnKeyDone;
|
|
self.phoneTextField.returnKeyType = UIReturnKeyNext;
|
|
|
|
self.userLoginTextField.placeholder = [VectorL10n authUserIdPlaceholder];
|
|
self.messageLabel.text = [VectorL10n or];
|
|
self.phoneTextField.placeholder = [VectorL10n authPhonePlaceholder];
|
|
|
|
self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.userLoginTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.phoneTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
|
|
self.userLoginContainer.hidden = NO;
|
|
self.messageLabel.hidden = !showPhoneTextField;
|
|
self.phoneContainer.hidden = !showPhoneTextField;
|
|
self.passwordContainer.hidden = NO;
|
|
|
|
self.messageLabelTopConstraint.constant = 59;
|
|
|
|
CGFloat phoneContainerTopConstraintConstant = 0.0;
|
|
CGFloat passwordContainerTopConstraintConstant = 0.0;
|
|
|
|
if (showPhoneTextField)
|
|
{
|
|
phoneContainerTopConstraintConstant = 70;
|
|
passwordContainerTopConstraintConstant = 150;
|
|
}
|
|
else
|
|
{
|
|
passwordContainerTopConstraintConstant = 50;
|
|
}
|
|
|
|
self.phoneContainerTopConstraint.constant = phoneContainerTopConstraintConstant;
|
|
self.passwordContainerTopConstraint.constant = passwordContainerTopConstraintConstant;
|
|
|
|
self.currentLastContainer = self.passwordContainer;
|
|
}
|
|
else if ([self isFlowSupported:kMXLoginFlowTypeCAS]
|
|
|| [self isFlowSupported:kMXLoginFlowTypeSSO])
|
|
{
|
|
|
|
self.ssoButtonContainer.hidden = NO;
|
|
self.currentLastContainer = self.ssoButtonContainer;
|
|
|
|
_isSingleSignOnRequired = YES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update the registration inputs layout by hidding third-party ids fields.
|
|
self.thirdPartyIdentifiersHidden = _thirdPartyIdentifiersHidden;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (NSString*)validateParameters
|
|
{
|
|
// Consider everything is fine when external registration parameters are ready to use
|
|
if (externalRegistrationParameters)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
// Check the validity of the parameters
|
|
NSString *errorMsg = nil;
|
|
|
|
// Remove whitespace in user login text field
|
|
NSString *userLogin = self.userLoginTextField.text;
|
|
self.userLoginTextField.text = [userLogin stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
|
|
if (type == MXKAuthenticationTypeLogin)
|
|
{
|
|
if ([self isFlowSupported:kMXLoginFlowTypePassword])
|
|
{
|
|
// Check required fields
|
|
if ((!self.userLoginTextField.text.length && !nbPhoneNumber) || !self.passWordTextField.text.length)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Invalid user/password");
|
|
errorMsg = [VectorL10n authInvalidLoginParam];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorMsg = [VectorL10n notSupportedYet];
|
|
}
|
|
}
|
|
else if (type == MXKAuthenticationTypeRegister)
|
|
{
|
|
if (self.isThirdPartyIdentifiersHidden)
|
|
{
|
|
if (!self.userLoginTextField.text.length)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Invalid user name");
|
|
errorMsg = [VectorL10n authInvalidUserName];
|
|
}
|
|
else if (!self.passWordTextField.text.length)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Missing Passwords");
|
|
errorMsg = [VectorL10n authMissingPassword];
|
|
}
|
|
else if (self.passWordTextField.text.length < 6)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Invalid Passwords");
|
|
errorMsg = [VectorL10n authInvalidPassword];
|
|
}
|
|
else if ([self.repeatPasswordTextField.text isEqualToString:self.passWordTextField.text] == NO)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Passwords don't match");
|
|
errorMsg = [VectorL10n authPasswordDontMatch];
|
|
}
|
|
else
|
|
{
|
|
// Check validity of the non empty user name
|
|
NSString *user = self.userLoginTextField.text;
|
|
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[a-z0-9.\\-_]+$" options:NSRegularExpressionCaseInsensitive error:nil];
|
|
|
|
if ([regex firstMatchInString:user options:0 range:NSMakeRange(0, user.length)] == nil)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Invalid user name");
|
|
errorMsg = [VectorL10n authInvalidUserName];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check email field
|
|
if ([self isFlowSupported:kMXLoginFlowTypeEmailIdentity] && !self.emailTextField.text.length)
|
|
{
|
|
if (self.areAllThirdPartyIdentifiersRequired)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Missing email");
|
|
errorMsg = [VectorL10n authMissingEmail];
|
|
}
|
|
else if ([self isFlowSupported:kMXLoginFlowTypeMSISDN] && !self.phoneTextField.text.length && self.isThirdPartyIdentifierRequired)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Missing email or phone number");
|
|
errorMsg = [VectorL10n authMissingEmailOrPhone];
|
|
}
|
|
}
|
|
|
|
if (!errorMsg)
|
|
{
|
|
// Check phone field
|
|
if ([self isFlowSupported:kMXLoginFlowTypeMSISDN] && !self.phoneTextField.text.length)
|
|
{
|
|
if (self.areAllThirdPartyIdentifiersRequired)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Missing phone");
|
|
errorMsg = [VectorL10n authMissingPhone];
|
|
}
|
|
}
|
|
|
|
if (!errorMsg)
|
|
{
|
|
// Check email/phone validity
|
|
if (self.emailTextField.text.length)
|
|
{
|
|
// Check validity of the non empty email
|
|
if (![MXTools isEmailAddress:self.emailTextField.text])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Invalid email");
|
|
errorMsg = [VectorL10n authInvalidEmail];
|
|
}
|
|
}
|
|
|
|
if (!errorMsg && nbPhoneNumber)
|
|
{
|
|
// Check validity of the non empty phone
|
|
if (![[NBPhoneNumberUtil sharedInstance] isValidNumber:nbPhoneNumber])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Invalid phone number");
|
|
errorMsg = [VectorL10n authInvalidPhone];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return errorMsg;
|
|
}
|
|
|
|
- (void)prepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
|
|
{
|
|
if (callback)
|
|
{
|
|
// Return external registration parameters if any
|
|
if (externalRegistrationParameters)
|
|
{
|
|
// We trigger here a registration based on external inputs. All the required data are handled by the session id.
|
|
MXLogDebug(@"[AuthInputsView] prepareParameters: return external registration parameters");
|
|
callback(externalRegistrationParameters, nil);
|
|
|
|
// CAUTION: Do not reset this dictionary here, it is used later to handle this registration until the end (see [updateAuthSessionWithCompletedStages:didUpdateParameters:])
|
|
|
|
return;
|
|
}
|
|
|
|
// Prepare here parameters dict by checking each required fields.
|
|
NSDictionary *parameters = nil;
|
|
|
|
// Check the validity of the parameters
|
|
NSString *errorMsg = [self validateParameters];
|
|
if (errorMsg)
|
|
{
|
|
if (inputsAlert)
|
|
{
|
|
[inputsAlert dismissViewControllerAnimated:NO completion:nil];
|
|
}
|
|
|
|
inputsAlert = [UIAlertController alertControllerWithTitle:[VectorL10n error] message:errorMsg preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
[inputsAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
self->inputsAlert = nil;
|
|
|
|
}]];
|
|
|
|
[self.delegate authInputsView:self presentAlertController:inputsAlert];
|
|
}
|
|
else
|
|
{
|
|
// Handle here the supported login flow
|
|
if (type == MXKAuthenticationTypeLogin)
|
|
{
|
|
if ([self isFlowSupported:kMXLoginFlowTypePassword])
|
|
{
|
|
// Check whether the user login has been set.
|
|
NSString *user = self.userLoginTextField.text;
|
|
|
|
if (user.length)
|
|
{
|
|
// Check whether user login is an email or a username.
|
|
if ([MXTools isEmailAddress:user])
|
|
{
|
|
parameters = @{
|
|
@"type": kMXLoginFlowTypePassword,
|
|
@"identifier": @{
|
|
@"type": kMXLoginIdentifierTypeThirdParty,
|
|
@"medium": kMX3PIDMediumEmail,
|
|
@"address": user
|
|
},
|
|
@"password": self.passWordTextField.text,
|
|
// Patch: add the old login api parameters for an email address (medium and address),
|
|
// to keep logging in against old HS.
|
|
@"medium": kMX3PIDMediumEmail,
|
|
@"address": user
|
|
};
|
|
}
|
|
else
|
|
{
|
|
parameters = @{
|
|
@"type": kMXLoginFlowTypePassword,
|
|
@"identifier": @{
|
|
@"type": kMXLoginIdentifierTypeUser,
|
|
@"user": user
|
|
},
|
|
@"password": self.passWordTextField.text,
|
|
// Patch: add the old login api parameters for a username (user),
|
|
// to keep logging in against old HS.
|
|
@"user": user
|
|
};
|
|
}
|
|
}
|
|
else if (nbPhoneNumber)
|
|
{
|
|
NSString *countryCode = [[NBPhoneNumberUtil sharedInstance] getRegionCodeForNumber:nbPhoneNumber];
|
|
NSString *e164 = [[NBPhoneNumberUtil sharedInstance] format:nbPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil];
|
|
NSString *msisdn;
|
|
if ([e164 hasPrefix:@"+"])
|
|
{
|
|
msisdn = [e164 substringFromIndex:1];
|
|
}
|
|
else if ([e164 hasPrefix:@"00"])
|
|
{
|
|
msisdn = [e164 substringFromIndex:2];
|
|
}
|
|
|
|
if (msisdn && countryCode)
|
|
{
|
|
parameters = @{
|
|
@"type": kMXLoginFlowTypePassword,
|
|
@"identifier": @{
|
|
@"type": kMXLoginIdentifierTypePhone,
|
|
@"country": countryCode,
|
|
@"number": msisdn
|
|
},
|
|
@"password": self.passWordTextField.text
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
// For soft logout, pass the device_id currently used
|
|
if (parameters && self.softLogoutCredentials)
|
|
{
|
|
NSMutableDictionary *parametersWithDeviceId = [parameters mutableCopy];
|
|
parametersWithDeviceId[@"device_id"] = self.softLogoutCredentials.deviceId;
|
|
parameters = parametersWithDeviceId;
|
|
}
|
|
|
|
}
|
|
else if (type == MXKAuthenticationTypeRegister)
|
|
{
|
|
// Check whether a phone number has been set, and if it is not handled yet
|
|
if (nbPhoneNumber && ![self isFlowCompleted:kMXLoginFlowTypeMSISDN])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Prepare msisdn stage");
|
|
|
|
// Retrieve the REST client from delegate
|
|
MXRestClient *restClient;
|
|
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewThirdPartyIdValidationRestClient:)])
|
|
{
|
|
restClient = [self.delegate authInputsViewThirdPartyIdValidationRestClient:self];
|
|
}
|
|
|
|
if (restClient)
|
|
{
|
|
MXWeakify(self);
|
|
[self checkIdentityServerRequirement:restClient success:^(BOOL identityServerRequired) {
|
|
MXStrongifyAndReturnIfNil(self);
|
|
|
|
if (identityServerRequired && !restClient.identityServer)
|
|
{
|
|
callback(nil, [NSError errorWithDomain:MXKAuthErrorDomain
|
|
code:0
|
|
userInfo:@{
|
|
NSLocalizedDescriptionKey:[VectorL10n authPhoneIsRequired]
|
|
}]);
|
|
return;
|
|
}
|
|
|
|
// Check whether a second 3pid is available
|
|
self->_isThirdPartyIdentifierPending = (!self.emailContainer.isHidden && self.emailTextField.text.length && ![self isFlowCompleted:kMXLoginFlowTypeEmailIdentity]);
|
|
|
|
// Launch msisdn validation
|
|
NSString *e164 = [[NBPhoneNumberUtil sharedInstance] format:self->nbPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil];
|
|
NSString *msisdn;
|
|
if ([e164 hasPrefix:@"+"])
|
|
{
|
|
msisdn = [e164 substringFromIndex:1];
|
|
}
|
|
else if ([e164 hasPrefix:@"00"])
|
|
{
|
|
msisdn = [e164 substringFromIndex:2];
|
|
}
|
|
self->submittedMSISDN = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumMSISDN andAddress:msisdn];
|
|
|
|
[self->submittedMSISDN requestValidationTokenWithMatrixRestClient:restClient
|
|
isDuringRegistration:YES
|
|
nextLink:nil
|
|
success:^
|
|
{
|
|
|
|
[self showValidationMSISDNDialogToPrepareParameters:callback];
|
|
|
|
}
|
|
failure:^(NSError *error)
|
|
{
|
|
|
|
MXLogDebug(@"[AuthInputsView] Failed to request msisdn token");
|
|
|
|
// Ignore connection cancellation error
|
|
if (([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Translate the potential MX error.
|
|
MXError *mxError = [[MXError alloc] initWithNSError:error];
|
|
if (mxError && ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse] || [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
|
|
{
|
|
NSMutableDictionary *userInfo;
|
|
if (error.userInfo)
|
|
{
|
|
userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
|
|
}
|
|
else
|
|
{
|
|
userInfo = [NSMutableDictionary dictionary];
|
|
}
|
|
|
|
userInfo[NSLocalizedFailureReasonErrorKey] = nil;
|
|
|
|
if ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse])
|
|
{
|
|
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authPhoneInUse];
|
|
userInfo[@"error"] = [VectorL10n authPhoneInUse];
|
|
}
|
|
else
|
|
{
|
|
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authUntrustedIdServer];
|
|
userInfo[@"error"] = [VectorL10n authUntrustedIdServer];
|
|
}
|
|
|
|
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
|
|
}
|
|
|
|
callback(nil, error);
|
|
|
|
}];
|
|
|
|
|
|
} failure:^(NSError *error) {
|
|
callback(nil, error);
|
|
}];
|
|
|
|
// Async response
|
|
return;
|
|
}
|
|
MXLogDebug(@"[AuthInputsView] Authentication failed during the msisdn stage");
|
|
}
|
|
// Check whether an email has been set, and if it is not handled yet
|
|
else if (!self.emailContainer.isHidden && self.emailTextField.text.length && ![self isFlowCompleted:kMXLoginFlowTypeEmailIdentity])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Prepare email identity stage");
|
|
|
|
// Retrieve the REST client from delegate
|
|
MXRestClient *restClient;
|
|
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewThirdPartyIdValidationRestClient:)])
|
|
{
|
|
restClient = [self.delegate authInputsViewThirdPartyIdValidationRestClient:self];
|
|
}
|
|
|
|
if (restClient)
|
|
{
|
|
MXWeakify(self);
|
|
[self checkIdentityServerRequirement:restClient success:^(BOOL identityServerRequired) {
|
|
MXStrongifyAndReturnIfNil(self);
|
|
|
|
if (identityServerRequired && !restClient.identityServer)
|
|
{
|
|
callback(nil, [NSError errorWithDomain:MXKAuthErrorDomain
|
|
code:0
|
|
userInfo:@{
|
|
NSLocalizedDescriptionKey:[VectorL10n authEmailIsRequired]
|
|
}]);
|
|
return;
|
|
}
|
|
|
|
// Check whether a second 3pid is available
|
|
self->_isThirdPartyIdentifierPending = (self->nbPhoneNumber && ![self isFlowCompleted:kMXLoginFlowTypeMSISDN]);
|
|
|
|
// Launch email validation
|
|
self->submittedEmail = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumEmail andAddress:self.emailTextField.text];
|
|
|
|
NSString *identityServer = restClient.identityServer;
|
|
|
|
[self->submittedEmail requestValidationTokenWithMatrixRestClient:restClient
|
|
isDuringRegistration:YES
|
|
nextLink:nil
|
|
success:^
|
|
{
|
|
NSMutableDictionary *threepidCreds = [NSMutableDictionary dictionaryWithDictionary:@{
|
|
@"client_secret": self->submittedEmail.clientSecret,
|
|
|
|
@"sid": self->submittedEmail.sid
|
|
}];
|
|
if (identityServer)
|
|
{
|
|
NSURL *identServerURL = [NSURL URLWithString:identityServer];
|
|
threepidCreds[@"id_server"] = identServerURL.host;
|
|
}
|
|
|
|
NSDictionary *parameters;
|
|
parameters = @{
|
|
@"auth": @{
|
|
@"session":self->currentSession.session,
|
|
@"threepid_creds": threepidCreds,
|
|
@"type": kMXLoginFlowTypeEmailIdentity},
|
|
@"username": self.userLoginTextField.text,
|
|
@"password": self.passWordTextField.text,
|
|
};
|
|
|
|
[self hideInputsContainer];
|
|
|
|
self.messageLabel.text = [VectorL10n authEmailValidationMessage];
|
|
self.messageLabel.hidden = NO;
|
|
|
|
callback(parameters, nil);
|
|
|
|
}
|
|
failure:^(NSError *error)
|
|
{
|
|
|
|
MXLogDebug(@"[AuthInputsView] Failed to request email token");
|
|
|
|
// Ignore connection cancellation error
|
|
if (([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Translate the potential MX error.
|
|
MXError *mxError = [[MXError alloc] initWithNSError:error];
|
|
if (mxError && ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse] || [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
|
|
{
|
|
NSMutableDictionary *userInfo;
|
|
if (error.userInfo)
|
|
{
|
|
userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
|
|
}
|
|
else
|
|
{
|
|
userInfo = [NSMutableDictionary dictionary];
|
|
}
|
|
|
|
userInfo[NSLocalizedFailureReasonErrorKey] = nil;
|
|
|
|
if ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse])
|
|
{
|
|
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authEmailInUse];
|
|
userInfo[@"error"] = [VectorL10n authEmailInUse];
|
|
}
|
|
else
|
|
{
|
|
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authUntrustedIdServer];
|
|
userInfo[@"error"] = [VectorL10n authUntrustedIdServer];
|
|
}
|
|
|
|
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
|
|
}
|
|
callback(nil, error);
|
|
|
|
}];
|
|
} failure:^(NSError *error) {
|
|
callback(nil, error);
|
|
}];
|
|
|
|
// Async response
|
|
return;
|
|
}
|
|
MXLogDebug(@"[AuthInputsView] Authentication failed during the email identity stage");
|
|
}
|
|
else if ([self isFlowSupported:kMXLoginFlowTypeRecaptcha] && ![self isFlowCompleted:kMXLoginFlowTypeRecaptcha])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Prepare reCaptcha stage");
|
|
|
|
[self displayRecaptchaForm:^(NSString *response) {
|
|
|
|
if (response.length)
|
|
{
|
|
NSDictionary *parameters = @{
|
|
@"auth": @{
|
|
@"session": self->currentSession.session,
|
|
@"response": response,
|
|
@"type": kMXLoginFlowTypeRecaptcha
|
|
},
|
|
@"username": self.userLoginTextField.text,
|
|
@"password": self.passWordTextField.text,
|
|
};
|
|
|
|
callback(parameters, nil);
|
|
}
|
|
else
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] reCaptcha stage failed");
|
|
callback(nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]);
|
|
}
|
|
|
|
}];
|
|
|
|
// Async response
|
|
return;
|
|
}
|
|
else if ([self isFlowSupported:kMXLoginFlowTypeDummy] && ![self isFlowCompleted:kMXLoginFlowTypeDummy])
|
|
{
|
|
parameters = @{
|
|
@"auth": @{
|
|
@"session":currentSession.session,
|
|
@"type": kMXLoginFlowTypeDummy
|
|
},
|
|
@"username": self.userLoginTextField.text,
|
|
@"password": self.passWordTextField.text,
|
|
};
|
|
}
|
|
else if ([self isFlowSupported:kMXLoginFlowTypePassword] && ![self isFlowCompleted:kMXLoginFlowTypePassword])
|
|
{
|
|
// Note: this use case was not tested yet.
|
|
parameters = @{
|
|
@"auth": @{
|
|
@"session":currentSession.session,
|
|
@"username": self.userLoginTextField.text,
|
|
@"password": self.passWordTextField.text,
|
|
@"type": kMXLoginFlowTypePassword
|
|
}
|
|
};
|
|
}
|
|
else if ([self isFlowSupported:kMXLoginFlowTypeTerms] && ![self isFlowCompleted:kMXLoginFlowTypeTerms])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Prepare terms stage");
|
|
|
|
MXWeakify(self);
|
|
[self displayTermsView:^{
|
|
MXStrongifyAndReturnIfNil(self);
|
|
|
|
NSDictionary *parameters = @{
|
|
@"auth": @{
|
|
@"session":self->currentSession.session,
|
|
@"type": kMXLoginFlowTypeTerms
|
|
},
|
|
@"username": self.userLoginTextField.text,
|
|
@"password": self.passWordTextField.text
|
|
};
|
|
callback(parameters, nil);
|
|
}];
|
|
|
|
// Async response
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
callback(parameters, nil);
|
|
}
|
|
}
|
|
|
|
- (void)updateAuthSessionWithCompletedStages:(NSArray *)completedStages didUpdateParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
|
|
{
|
|
if (callback)
|
|
{
|
|
if (currentSession)
|
|
{
|
|
currentSession.completed = completedStages;
|
|
|
|
BOOL isMSISDNFlowCompleted = [self isFlowCompleted:kMXLoginFlowTypeMSISDN];
|
|
BOOL isEmailFlowCompleted = [self isFlowCompleted:kMXLoginFlowTypeEmailIdentity];
|
|
|
|
// Check the supported use cases
|
|
if (isMSISDNFlowCompleted && self.isThirdPartyIdentifierPending)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Prepare a new third-party stage");
|
|
|
|
// Here an email address is available, we add it to the authentication session.
|
|
[self prepareParameters:callback];
|
|
|
|
return;
|
|
}
|
|
else if ((isMSISDNFlowCompleted || isEmailFlowCompleted)
|
|
&& [self isFlowSupported:kMXLoginFlowTypeRecaptcha] && ![self isFlowCompleted:kMXLoginFlowTypeRecaptcha])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Display reCaptcha stage");
|
|
|
|
if (externalRegistrationParameters)
|
|
{
|
|
[self displayRecaptchaForm:^(NSString *response) {
|
|
|
|
if (response.length)
|
|
{
|
|
// We finalize here a registration triggered from external inputs. All the required data are handled by the session id
|
|
NSDictionary *parameters = @{
|
|
@"auth": @{@"session": self->currentSession.session, @"response": response, @"type": kMXLoginFlowTypeRecaptcha},
|
|
};
|
|
callback (parameters, nil);
|
|
}
|
|
else
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] reCaptcha stage failed");
|
|
callback (nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]);
|
|
}
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
[self prepareParameters:callback];
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if ([self isFlowSupported:kMXLoginFlowTypeTerms] && ![self isFlowCompleted:kMXLoginFlowTypeTerms])
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Prepare a new terms stage");
|
|
|
|
if (externalRegistrationParameters)
|
|
{
|
|
[self displayTermsView:^{
|
|
|
|
NSDictionary *parameters = @{
|
|
@"auth": @{
|
|
@"session":self->currentSession.session,
|
|
@"type": kMXLoginFlowTypeTerms
|
|
}
|
|
};
|
|
callback(parameters, nil);
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
[self prepareParameters:callback];
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
MXLogDebug(@"[AuthInputsView] updateAuthSessionWithCompletedStages failed");
|
|
callback (nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]);
|
|
}
|
|
}
|
|
|
|
- (BOOL)setExternalRegistrationParameters:(NSDictionary *)registrationParameters
|
|
{
|
|
// Presently we only support a registration based on next_link associated to a successful email validation.
|
|
NSString *homeserverURL;
|
|
NSString *identityURL;
|
|
|
|
// Check the current authentication type
|
|
if (self.authType != MXKAuthenticationTypeRegister)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] setExternalRegistrationParameters failed: wrong auth type");
|
|
return NO;
|
|
}
|
|
|
|
// Retrieve the REST client from delegate
|
|
MXRestClient *restClient;
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewThirdPartyIdValidationRestClient:)])
|
|
{
|
|
restClient = [self.delegate authInputsViewThirdPartyIdValidationRestClient:self];
|
|
}
|
|
|
|
if (restClient)
|
|
{
|
|
// Sanity check on homeserver
|
|
id hs_url = registrationParameters[@"hs_url"];
|
|
if (hs_url && [hs_url isKindOfClass:NSString.class])
|
|
{
|
|
homeserverURL = hs_url;
|
|
|
|
if ([homeserverURL isEqualToString:restClient.homeserver] == NO)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] setExternalRegistrationParameters failed: wrong homeserver URL");
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
// Sanity check on identity server
|
|
id is_url = registrationParameters[@"is_url"];
|
|
if (is_url && [is_url isKindOfClass:NSString.class])
|
|
{
|
|
identityURL = is_url;
|
|
|
|
if ([identityURL isEqualToString:restClient.identityServer] == NO)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] setExternalRegistrationParameters failed: wrong identity server URL");
|
|
return NO;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] setExternalRegistrationParameters failed: not supported");
|
|
return NO;
|
|
}
|
|
|
|
// Retrieve other parameters
|
|
NSString *clientSecret;
|
|
NSString *sid;
|
|
NSString *sessionId;
|
|
|
|
id value = registrationParameters[@"client_secret"];
|
|
if (value && [value isKindOfClass:NSString.class])
|
|
{
|
|
clientSecret = value;
|
|
}
|
|
value = registrationParameters[@"sid"];
|
|
if (value && [value isKindOfClass:NSString.class])
|
|
{
|
|
sid = value;
|
|
}
|
|
value = registrationParameters[@"session_id"];
|
|
if (value && [value isKindOfClass:NSString.class])
|
|
{
|
|
sessionId = value;
|
|
}
|
|
|
|
// Check validity of the required parameters
|
|
if (!homeserverURL.length || !clientSecret.length || !sid.length || !sessionId.length)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] setExternalRegistrationParameters failed: wrong parameters");
|
|
return NO;
|
|
}
|
|
|
|
// Prepare the registration parameters (Ready to use)
|
|
|
|
NSMutableDictionary *threepidCreds = [NSMutableDictionary dictionaryWithDictionary:@{
|
|
@"client_secret": clientSecret,
|
|
|
|
@"sid": sid
|
|
}];
|
|
if (identityURL)
|
|
{
|
|
NSURL *identServerURL = [NSURL URLWithString:identityURL];
|
|
threepidCreds[@"id_server"] = identServerURL.host;
|
|
}
|
|
|
|
externalRegistrationParameters = @{
|
|
@"auth": @{
|
|
@"session": sessionId,
|
|
@"threepid_creds": threepidCreds,
|
|
@"type": kMXLoginFlowTypeEmailIdentity
|
|
},
|
|
};
|
|
|
|
// Hide all inputs by default
|
|
[self hideInputsContainer];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)setSoftLogoutCredentials:(MXCredentials *)credentials
|
|
{
|
|
softLogoutCredentials = credentials;
|
|
self.userLoginTextField.text = softLogoutCredentials.userId;
|
|
self.userLoginContainer.hidden = YES;
|
|
self.phoneContainer.hidden = YES;
|
|
|
|
[self displaySoftLogoutMessage];
|
|
}
|
|
|
|
- (void)displaySoftLogoutMessage
|
|
{
|
|
// Backup is now handled by Rust
|
|
BOOL keyBackupNeeded = NO;
|
|
|
|
MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:softLogoutCredentials];
|
|
[fileStore asyncUsersWithUserIds:@[softLogoutCredentials.userId] success:^(NSArray<MXUser *> * _Nonnull users) {
|
|
|
|
MXUser *myUser = users.firstObject;
|
|
[fileStore close];
|
|
|
|
[self displaySoftLogoutMessageWithUserDisplayname:myUser.displayname andKeyBackupNeeded:keyBackupNeeded];
|
|
|
|
} failure:^(NSError * _Nonnull error) {
|
|
MXLogDebug(@"[AuthInputsView] displaySoftLogoutMessage: Cannot load displayname. Error: %@", error);
|
|
[self displaySoftLogoutMessageWithUserDisplayname:nil andKeyBackupNeeded:keyBackupNeeded];
|
|
}];
|
|
}
|
|
|
|
- (void)displaySoftLogoutMessageWithUserDisplayname:(NSString*)userDisplayname andKeyBackupNeeded:(BOOL)keyBackupNeeded
|
|
{
|
|
// Use messageLabel for this message
|
|
self.messageLabelTopConstraint.constant = 8;
|
|
self.messageLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
self.messageLabel.hidden = NO;
|
|
|
|
NSMutableAttributedString *message = [[NSMutableAttributedString alloc] initWithString:[VectorL10n authSoftlogoutSignIn]
|
|
attributes:@{
|
|
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]
|
|
}];
|
|
|
|
[message appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]];
|
|
|
|
NSString *string = [VectorL10n authSoftlogoutReason:softLogoutCredentials.homeServerName :userDisplayname :softLogoutCredentials.userId];
|
|
[message appendAttributedString:[[NSAttributedString alloc] initWithString:string
|
|
attributes:@{
|
|
NSFontAttributeName: [UIFont systemFontOfSize:14]
|
|
}]];
|
|
|
|
if (keyBackupNeeded)
|
|
{
|
|
[message appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]];
|
|
string = [VectorL10n authSoftlogoutRecoverEncryptionKeys];
|
|
[message appendAttributedString:[[NSAttributedString alloc] initWithString:string
|
|
attributes:@{
|
|
NSFontAttributeName: [UIFont systemFontOfSize:14]
|
|
}]];
|
|
}
|
|
|
|
self.messageLabel.attributedText = message;
|
|
}
|
|
|
|
- (BOOL)areAllRequiredFieldsSet
|
|
{
|
|
// Keep enable the submit button.
|
|
return YES;
|
|
}
|
|
|
|
- (void)dismissKeyboard
|
|
{
|
|
[self.userLoginTextField resignFirstResponder];
|
|
[self.passWordTextField resignFirstResponder];
|
|
[self.emailTextField resignFirstResponder];
|
|
[self.phoneTextField resignFirstResponder];
|
|
[self.repeatPasswordTextField resignFirstResponder];
|
|
|
|
[super dismissKeyboard];
|
|
}
|
|
|
|
- (void)dismissCountryPicker
|
|
{
|
|
[phoneNumberCountryPicker withdrawViewControllerAnimated:YES completion:nil];
|
|
[phoneNumberCountryPicker destroy];
|
|
phoneNumberCountryPicker = nil;
|
|
|
|
[phoneNumberPickerNavigationController dismissViewControllerAnimated:YES completion:nil];
|
|
phoneNumberPickerNavigationController = nil;
|
|
}
|
|
|
|
- (NSString*)userId
|
|
{
|
|
return self.userLoginTextField.text;
|
|
}
|
|
|
|
- (NSString*)password
|
|
{
|
|
return self.passWordTextField.text;
|
|
}
|
|
|
|
- (void)setCurrentLastContainer:(UIView*)currentLastContainer
|
|
{
|
|
_currentLastContainer = currentLastContainer;
|
|
|
|
CGRect frame = _currentLastContainer.frame;
|
|
self.viewHeightConstraint.constant = frame.origin.y + frame.size.height;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (BOOL)areThirdPartyIdentifiersSupported
|
|
{
|
|
return ([self isFlowSupported:kMXLoginFlowTypeEmailIdentity] || [self isFlowSupported:kMXLoginFlowTypeMSISDN]);
|
|
}
|
|
|
|
- (BOOL)isThirdPartyIdentifierRequired
|
|
{
|
|
// Check first whether some 3pids are supported
|
|
if (!self.areThirdPartyIdentifiersSupported)
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
// Check whether an account may be created without third-party identifiers.
|
|
for (MXLoginFlow *loginFlow in currentSession.flows)
|
|
{
|
|
if ([loginFlow.stages indexOfObject:kMXLoginFlowTypeEmailIdentity] == NSNotFound
|
|
&& [loginFlow.stages indexOfObject:kMXLoginFlowTypeMSISDN] == NSNotFound)
|
|
{
|
|
// There is a flow with no 3pids
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)areAllThirdPartyIdentifiersRequired
|
|
{
|
|
// Check first whether some 3pids are required
|
|
if (!self.isThirdPartyIdentifierRequired)
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
BOOL isEmailIdentityFlowSupported = [self isFlowSupported:kMXLoginFlowTypeEmailIdentity];
|
|
BOOL isMSISDNFlowSupported = [self isFlowSupported:kMXLoginFlowTypeMSISDN];
|
|
|
|
for (MXLoginFlow *loginFlow in currentSession.flows)
|
|
{
|
|
if (isEmailIdentityFlowSupported)
|
|
{
|
|
if ([loginFlow.stages indexOfObject:kMXLoginFlowTypeEmailIdentity] == NSNotFound)
|
|
{
|
|
return NO;
|
|
}
|
|
else if (isMSISDNFlowSupported)
|
|
{
|
|
if ([loginFlow.stages indexOfObject:kMXLoginFlowTypeMSISDN] == NSNotFound)
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
}
|
|
else if (isMSISDNFlowSupported)
|
|
{
|
|
if ([loginFlow.stages indexOfObject:kMXLoginFlowTypeMSISDN] == NSNotFound)
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)setThirdPartyIdentifiersHidden:(BOOL)thirdPartyIdentifiersHidden
|
|
{
|
|
[self hideInputsContainer];
|
|
|
|
UIView *lastViewContainer;
|
|
|
|
if (thirdPartyIdentifiersHidden)
|
|
{
|
|
self.passWordTextField.returnKeyType = UIReturnKeyNext;
|
|
|
|
self.userLoginTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:[VectorL10n authUserNamePlaceholder]
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
|
|
self.userLoginContainer.hidden = NO;
|
|
self.passwordContainer.hidden = NO;
|
|
self.repeatPasswordContainer.hidden = NO;
|
|
|
|
self.passwordContainerTopConstraint.constant = 50;
|
|
|
|
lastViewContainer = self.repeatPasswordContainer;
|
|
}
|
|
else
|
|
{
|
|
if ([self isFlowSupported:kMXLoginFlowTypeEmailIdentity])
|
|
{
|
|
if (self.isThirdPartyIdentifierRequired)
|
|
{
|
|
self.emailTextField.placeholder = [VectorL10n authEmailPlaceholder];
|
|
}
|
|
else
|
|
{
|
|
self.emailTextField.placeholder = [VectorL10n authOptionalEmailPlaceholder];
|
|
}
|
|
|
|
self.emailTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.emailTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
|
|
self.emailContainer.hidden = NO;
|
|
|
|
self.messageLabel.hidden = NO;
|
|
self.messageLabel.text = [VectorL10n authAddEmailMessage2];
|
|
|
|
lastViewContainer = self.emailContainer;
|
|
}
|
|
|
|
if ([self isFlowSupported:kMXLoginFlowTypeMSISDN])
|
|
{
|
|
self.phoneTextField.returnKeyType = UIReturnKeyDone;
|
|
|
|
if (self.isThirdPartyIdentifierRequired)
|
|
{
|
|
self.phoneTextField.placeholder = [VectorL10n authPhonePlaceholder];
|
|
}
|
|
else
|
|
{
|
|
self.phoneTextField.placeholder = [VectorL10n authOptionalPhonePlaceholder];
|
|
}
|
|
|
|
self.phoneTextField.attributedPlaceholder = [[NSAttributedString alloc]
|
|
initWithString:self.phoneTextField.placeholder
|
|
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
|
|
|
|
self.phoneContainer.hidden = NO;
|
|
|
|
if (!_emailContainer.isHidden)
|
|
{
|
|
self.emailTextField.returnKeyType = UIReturnKeyNext;
|
|
|
|
self.phoneContainerTopConstraint.constant = 50;
|
|
self.messageLabel.text = [VectorL10n authAddEmailPhoneMessage2];
|
|
}
|
|
else
|
|
{
|
|
self.phoneContainerTopConstraint.constant = 0;
|
|
|
|
self.messageLabel.hidden = NO;
|
|
self.messageLabel.text = [VectorL10n authAddPhoneMessage2];
|
|
}
|
|
|
|
lastViewContainer = self.phoneContainer;
|
|
}
|
|
|
|
if (!self.messageLabel.isHidden)
|
|
{
|
|
[self.messageLabel sizeToFit];
|
|
|
|
CGRect frame = self.messageLabel.frame;
|
|
|
|
CGFloat offset = frame.origin.y + frame.size.height;
|
|
|
|
self.emailContainerTopConstraint.constant = offset;
|
|
self.phoneContainerTopConstraint.constant += offset;
|
|
}
|
|
}
|
|
|
|
self.currentLastContainer = lastViewContainer;
|
|
|
|
_thirdPartyIdentifiersHidden = thirdPartyIdentifiersHidden;
|
|
}
|
|
|
|
- (IBAction)selectPhoneNumberCountry:(id)sender
|
|
{
|
|
if ([self.delegate respondsToSelector:@selector(authInputsView:presentViewController:animated:)])
|
|
{
|
|
phoneNumberCountryPicker = [CountryPickerViewController countryPickerViewController];
|
|
phoneNumberCountryPicker.delegate = self;
|
|
phoneNumberCountryPicker.showCountryCallingCode = YES;
|
|
|
|
phoneNumberPickerNavigationController = [[RiotNavigationController alloc] init];
|
|
|
|
// Set Riot navigation bar colors
|
|
[ThemeService.shared.theme applyStyleOnNavigationBar:phoneNumberPickerNavigationController.navigationBar];
|
|
|
|
[phoneNumberPickerNavigationController pushViewController:phoneNumberCountryPicker animated:NO];
|
|
|
|
UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:AssetImages.backIcon.image style:UIBarButtonItemStylePlain target:self action:@selector(dismissCountryPicker)];
|
|
phoneNumberCountryPicker.navigationItem.leftBarButtonItem = leftBarButtonItem;
|
|
|
|
[self.delegate authInputsView:self presentViewController:phoneNumberPickerNavigationController animated:YES];
|
|
}
|
|
}
|
|
|
|
- (void)setIsoCountryCode:(NSString *)isoCountryCode
|
|
{
|
|
_isoCountryCode = isoCountryCode;
|
|
|
|
NSNumber *callingCode = [[NBPhoneNumberUtil sharedInstance] getCountryCodeForRegion:isoCountryCode];
|
|
|
|
self.callingCodeLabel.text = [NSString stringWithFormat:@"+%@", callingCode.stringValue];
|
|
|
|
self.isoCountryCodeLabel.text = isoCountryCode;
|
|
|
|
// Update displayed phone
|
|
[self textFieldDidChange:self.phoneTextField];
|
|
}
|
|
|
|
- (void)resetThirdPartyIdentifiers
|
|
{
|
|
[self dismissKeyboard];
|
|
|
|
self.emailTextField.text = nil;
|
|
self.phoneTextField.text = nil;
|
|
|
|
nbPhoneNumber = nil;
|
|
}
|
|
|
|
#pragma mark - MXKCountryPickerViewControllerDelegate
|
|
|
|
- (void)countryPickerViewController:(MXKCountryPickerViewController *)countryPickerViewController didSelectCountry:(NSString *)isoCountryCode
|
|
{
|
|
self.isoCountryCode = isoCountryCode;
|
|
|
|
nbPhoneNumber = [[NBPhoneNumberUtil sharedInstance] parse:self.phoneTextField.text defaultRegion:isoCountryCode error:nil];
|
|
[self formatNewPhoneNumber];
|
|
|
|
[self dismissCountryPicker];
|
|
}
|
|
|
|
#pragma mark - UITextField delegate
|
|
|
|
- (void)textFieldDidEndEditing:(UITextField*)textField
|
|
{
|
|
if (textField == self.userLoginTextField && type == MXKAuthenticationTypeLogin)
|
|
{
|
|
if ([MXTools isMatrixUserIdentifier:self.userLoginTextField.text])
|
|
{
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsView:autoDiscoverServerWithDomain:)])
|
|
{
|
|
NSString *domain = [self.userLoginTextField.text componentsSeparatedByString:@":"][1];
|
|
[self.delegate authInputsView:self autoDiscoverServerWithDomain:domain];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
|
{
|
|
if (textField.returnKeyType == UIReturnKeyDone)
|
|
{
|
|
// "Done" key has been pressed
|
|
[textField resignFirstResponder];
|
|
|
|
// Launch authentication now
|
|
[self.delegate authInputsViewDidPressDoneKey:self];
|
|
}
|
|
else
|
|
{
|
|
//"Next" key has been pressed
|
|
if (textField == self.userLoginTextField || textField == self.phoneTextField)
|
|
{
|
|
[self.passWordTextField becomeFirstResponder];
|
|
}
|
|
else if (textField == self.passWordTextField)
|
|
{
|
|
[self.repeatPasswordTextField becomeFirstResponder];
|
|
}
|
|
else if (textField == self.emailTextField)
|
|
{
|
|
[self.phoneTextField becomeFirstResponder];
|
|
}
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark - TextField listener
|
|
|
|
- (IBAction)textFieldDidChange:(id)sender
|
|
{
|
|
UITextField* textField = (UITextField*)sender;
|
|
|
|
if (textField == self.phoneTextField)
|
|
{
|
|
nbPhoneNumber = [[NBPhoneNumberUtil sharedInstance] parse:self.phoneTextField.text defaultRegion:self.isoCountryCode error:nil];
|
|
|
|
[self formatNewPhoneNumber];
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)hideInputsContainer
|
|
{
|
|
// Hide all inputs container
|
|
self.userLoginContainer.hidden = YES;
|
|
self.passwordContainer.hidden = YES;
|
|
self.emailContainer.hidden = YES;
|
|
self.phoneContainer.hidden = YES;
|
|
self.repeatPasswordContainer.hidden = YES;
|
|
|
|
// Hide other items
|
|
self.messageLabelTopConstraint.constant = 8;
|
|
self.messageLabel.hidden = YES;
|
|
self.recaptchaContainer.hidden = YES;
|
|
self.termsView.hidden = YES;
|
|
self.ssoButtonContainer.hidden = YES;
|
|
|
|
_currentLastContainer = nil;
|
|
}
|
|
|
|
- (void)formatNewPhoneNumber
|
|
{
|
|
if (nbPhoneNumber)
|
|
{
|
|
NSString *formattedNumber = [[NBPhoneNumberUtil sharedInstance] format:nbPhoneNumber numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:nil];
|
|
NSString *prefix = self.callingCodeLabel.text;
|
|
if ([formattedNumber hasPrefix:prefix])
|
|
{
|
|
// Format the display phone number
|
|
self.phoneTextField.text = [formattedNumber substringFromIndex:prefix.length];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)displayRecaptchaForm:(void (^)(NSString *response))callback
|
|
{
|
|
// Retrieve the site key
|
|
NSString *siteKey;
|
|
|
|
id recaptchaParams = currentSession.params[kMXLoginFlowTypeRecaptcha];
|
|
if (recaptchaParams && [recaptchaParams isKindOfClass:NSDictionary.class])
|
|
{
|
|
NSDictionary *recaptchaParamsDict = (NSDictionary*)recaptchaParams;
|
|
siteKey = recaptchaParamsDict[@"public_key"];
|
|
}
|
|
|
|
// Retrieve the REST client from delegate
|
|
MXRestClient *restClient;
|
|
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewThirdPartyIdValidationRestClient:)])
|
|
{
|
|
restClient = [self.delegate authInputsViewThirdPartyIdValidationRestClient:self];
|
|
}
|
|
|
|
// Sanity check
|
|
if (siteKey.length && restClient && callback)
|
|
{
|
|
[self hideInputsContainer];
|
|
|
|
self.messageLabel.hidden = NO;
|
|
self.messageLabel.text = [VectorL10n authRecaptchaMessage];
|
|
|
|
self.recaptchaContainer.hidden = NO;
|
|
self.currentLastContainer = self.recaptchaContainer;
|
|
|
|
// IB does not support WKWebview in a xib before iOS 11
|
|
// So, add it by coding
|
|
|
|
// Do some cleaning/reset before
|
|
for (UIView *view in self.recaptchaContainer.subviews)
|
|
{
|
|
[view removeFromSuperview];
|
|
}
|
|
|
|
MXKAuthenticationRecaptchaWebView *reCaptchaWebView = [MXKAuthenticationRecaptchaWebView new];
|
|
reCaptchaWebView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
[self.recaptchaContainer addSubview:reCaptchaWebView];
|
|
|
|
// Disable the webview scrollView to avoid 2 scrollviews on the same screen
|
|
reCaptchaWebView.scrollView.scrollEnabled = NO;
|
|
|
|
[self.recaptchaContainer addConstraints:
|
|
[NSLayoutConstraint constraintsWithVisualFormat:@"|-[view]-|"
|
|
options:0
|
|
metrics:0
|
|
views:@{
|
|
@"view": reCaptchaWebView
|
|
}
|
|
]
|
|
];
|
|
[self.recaptchaContainer addConstraints:
|
|
[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]-|"
|
|
options:0
|
|
metrics:0
|
|
views:@{
|
|
@"view": reCaptchaWebView
|
|
}
|
|
]
|
|
];
|
|
|
|
|
|
[reCaptchaWebView openRecaptchaWidgetWithSiteKey:siteKey fromHomeServer:restClient.homeserver callback:callback];
|
|
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
// Tell whether a flow type is supported or not by this view.
|
|
- (BOOL)isSupportedFlowType:(MXLoginFlowType)flowType
|
|
{
|
|
if ([flowType isEqualToString:kMXLoginFlowTypePassword])
|
|
{
|
|
return YES;
|
|
}
|
|
else if ([flowType isEqualToString:kMXLoginFlowTypeEmailIdentity])
|
|
{
|
|
return YES;
|
|
}
|
|
else if ([flowType isEqualToString:kMXLoginFlowTypeRecaptcha])
|
|
{
|
|
return YES;
|
|
}
|
|
else if ([flowType isEqualToString:kMXLoginFlowTypeMSISDN])
|
|
{
|
|
return YES;
|
|
}
|
|
else if ([flowType isEqualToString:kMXLoginFlowTypeDummy])
|
|
{
|
|
return YES;
|
|
}
|
|
else if ([flowType isEqualToString:kMXLoginFlowTypeTerms])
|
|
{
|
|
return YES;
|
|
}
|
|
else if ([flowType isEqualToString:kMXLoginFlowTypeCAS] || [flowType isEqualToString:kMXLoginFlowTypeSSO])
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (MXAuthenticationSession*)validateAuthenticationSession:(MXAuthenticationSession*)authSession
|
|
{
|
|
// Check whether the listed flows in this authentication session are supported
|
|
NSMutableArray *supportedFlows = [NSMutableArray array];
|
|
|
|
for (MXLoginFlow* flow in authSession.flows)
|
|
{
|
|
// Check whether flow type is defined
|
|
if (flow.type)
|
|
{
|
|
if ([self isSupportedFlowType:flow.type])
|
|
{
|
|
// Check here all stages
|
|
BOOL isSupported = YES;
|
|
if (flow.stages.count)
|
|
{
|
|
for (NSString *stage in flow.stages)
|
|
{
|
|
if ([self isSupportedFlowType:stage] == NO)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] %@: %@ stage is not supported.", (type == MXKAuthenticationTypeLogin ? @"login" : @"register"), stage);
|
|
isSupported = NO;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flow.stages = @[flow.type];
|
|
}
|
|
|
|
if (isSupported)
|
|
{
|
|
[supportedFlows addObject:flow];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] %@: %@ stage is not supported.", (type == MXKAuthenticationTypeLogin ? @"login" : @"register"), flow.type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check here all stages
|
|
BOOL isSupported = YES;
|
|
if (flow.stages.count)
|
|
{
|
|
for (NSString *stage in flow.stages)
|
|
{
|
|
if ([self isSupportedFlowType:stage] == NO)
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] %@: %@ stage is not supported.", (type == MXKAuthenticationTypeLogin ? @"login" : @"register"), stage);
|
|
isSupported = NO;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isSupported)
|
|
{
|
|
[supportedFlows addObject:flow];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (supportedFlows.count)
|
|
{
|
|
if (supportedFlows.count == authSession.flows.count)
|
|
{
|
|
// Return the original session.
|
|
return authSession;
|
|
}
|
|
else
|
|
{
|
|
// Keep only the supported flow.
|
|
MXAuthenticationSession *updatedAuthSession = [[MXAuthenticationSession alloc] init];
|
|
updatedAuthSession.session = authSession.session;
|
|
updatedAuthSession.params = authSession.params;
|
|
updatedAuthSession.flows = supportedFlows;
|
|
return updatedAuthSession;
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)showValidationMSISDNDialogToPrepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
|
|
{
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
if (inputsAlert)
|
|
{
|
|
[inputsAlert dismissViewControllerAnimated:NO completion:nil];
|
|
}
|
|
|
|
if (inputsAlert)
|
|
{
|
|
[inputsAlert dismissViewControllerAnimated:NO completion:nil];
|
|
}
|
|
|
|
inputsAlert = [UIAlertController alertControllerWithTitle:[VectorL10n authMsisdnValidationTitle] message:[VectorL10n authMsisdnValidationMessage] preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
[inputsAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
if (weakSelf)
|
|
{
|
|
typeof(self) self = weakSelf;
|
|
self->inputsAlert = nil;
|
|
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewDidCancelOperation:)])
|
|
{
|
|
[self.delegate authInputsViewDidCancelOperation:self];
|
|
}
|
|
}
|
|
|
|
}]];
|
|
|
|
[inputsAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
|
|
|
textField.secureTextEntry = NO;
|
|
textField.placeholder = nil;
|
|
textField.keyboardType = UIKeyboardTypeDecimalPad;
|
|
|
|
}];
|
|
|
|
[inputsAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n submit]
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
if (weakSelf)
|
|
{
|
|
typeof(self) self = weakSelf;
|
|
UITextField *textField = [self->inputsAlert textFields].firstObject;
|
|
NSString *smsCode = textField.text;
|
|
self->inputsAlert = nil;
|
|
|
|
if (smsCode.length)
|
|
{
|
|
[self->submittedMSISDN submitValidationToken:smsCode success:^{
|
|
|
|
// Retrieve the identity service from delegate
|
|
MXIdentityService *identityService;
|
|
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewThirdPartyIdValidationIdentityService:)])
|
|
{
|
|
identityService = [self.delegate authInputsViewThirdPartyIdValidationIdentityService:self];
|
|
}
|
|
|
|
NSString *identityServer = identityService.identityServer;
|
|
|
|
if (identityServer)
|
|
{
|
|
NSURL *identServerURL = [NSURL URLWithString:identityServer];
|
|
NSDictionary *parameters;
|
|
parameters = @{
|
|
@"auth": @{
|
|
@"session":self->currentSession.session,
|
|
@"threepid_creds": @{
|
|
@"client_secret": self->submittedMSISDN.clientSecret,
|
|
@"id_server": identServerURL.host,
|
|
@"sid": self->submittedMSISDN.sid
|
|
},
|
|
@"type": kMXLoginFlowTypeMSISDN
|
|
},
|
|
@"username": self.userLoginTextField.text,
|
|
@"password": self.passWordTextField.text
|
|
};
|
|
|
|
callback(parameters, nil);
|
|
}
|
|
else
|
|
{
|
|
MXLogDebug(@"[AuthInputsView] Failed to retrieve identity server URL");
|
|
}
|
|
|
|
} failure:^(NSError *error) {
|
|
|
|
MXLogDebug(@"[AuthInputsView] Failed to submit the sms token");
|
|
|
|
// Ignore connection cancellation error
|
|
if (([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Alert user
|
|
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
|
|
NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
|
|
if (!title)
|
|
{
|
|
if (msg)
|
|
{
|
|
title = msg;
|
|
msg = nil;
|
|
}
|
|
else
|
|
{
|
|
title = [VectorL10n error];
|
|
}
|
|
}
|
|
|
|
self->inputsAlert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
[self->inputsAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
if (weakSelf)
|
|
{
|
|
typeof(self) self = weakSelf;
|
|
self->inputsAlert = nil;
|
|
|
|
// Ask again for the token
|
|
[self showValidationMSISDNDialogToPrepareParameters:callback];
|
|
}
|
|
|
|
}]];
|
|
|
|
[self->inputsAlert mxk_setAccessibilityIdentifier:@"AuthInputsViewErrorAlert"];
|
|
[self.delegate authInputsView:self presentAlertController:self->inputsAlert];
|
|
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
// Ask again for the token
|
|
[self showValidationMSISDNDialogToPrepareParameters:callback];
|
|
}
|
|
}
|
|
|
|
}]];
|
|
|
|
[inputsAlert mxk_setAccessibilityIdentifier:@"AuthInputsViewMsisdnValidationAlert"];
|
|
[self.delegate authInputsView:self presentAlertController:inputsAlert];
|
|
}
|
|
|
|
- (BOOL)displayTermsView:(dispatch_block_t)onAcceptedCallback
|
|
{
|
|
// Extract data
|
|
NSDictionary *loginTermsData = currentSession.params[kMXLoginFlowTypeTerms];
|
|
MXLoginTerms *loginTerms;
|
|
MXJSONModelSetMXJSONModel(loginTerms, MXLoginTerms.class, loginTermsData);
|
|
|
|
if (loginTerms)
|
|
{
|
|
[self hideInputsContainer];
|
|
|
|
self.messageLabel.hidden = NO;
|
|
self.messageLabel.text = [VectorL10n authAcceptPolicies];
|
|
|
|
self.termsView.hidden = NO;
|
|
self.currentLastContainer = self.termsView;
|
|
|
|
self.termsView.delegate = self.delegate;
|
|
[self.termsView displayTermsWithTerms:loginTerms onAccepted:onAcceptedCallback];
|
|
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
#pragma mark - Flow state
|
|
|
|
/**
|
|
Check if a flow (kMXLoginFlowType*) is part of the required flows steps.
|
|
|
|
@param flow the flow type to check.
|
|
@return YES if the the flow must be implemented.
|
|
*/
|
|
- (BOOL)isFlowSupported:(NSString *)flow
|
|
{
|
|
for (MXLoginFlow *loginFlow in currentSession.flows)
|
|
{
|
|
if ([loginFlow.type isEqualToString:flow] || [loginFlow.stages indexOfObject:flow] != NSNotFound)
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
/**
|
|
Check if a flow (kMXLoginFlowType*) has already been completed.
|
|
|
|
@param flow the flow type to check.
|
|
@return YES if the the flow has been completedd.
|
|
*/
|
|
- (BOOL)isFlowCompleted:(NSString *)flow
|
|
{
|
|
if (currentSession.completed && [currentSession.completed indexOfObject:flow] != NSNotFound)
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)checkIdentityServerRequirement:(MXRestClient*)mxRestClient
|
|
success:(void (^)(BOOL identityServerRequired))success
|
|
failure:(void (^)(NSError *error))failure
|
|
{
|
|
[mxRestClient supportedMatrixVersions:^(MXMatrixVersions *matrixVersions) {
|
|
|
|
MXLogDebug(@"[AuthInputsView] checkIdentityServerRequirement: %@", matrixVersions.doesServerRequireIdentityServerParam ? @"YES": @"NO");
|
|
success(matrixVersions.doesServerRequireIdentityServerParam);
|
|
|
|
} failure:failure];
|
|
}
|
|
|
|
@end
|