No Description

AWSCognitoIdentityUser.m 95KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644
  1. //
  2. // Copyright 2014-2018 Amazon.com,
  3. // Inc. or its affiliates. All Rights Reserved.
  4. //
  5. // Licensed under the Amazon Software License (the "License").
  6. // You may not use this file except in compliance with the
  7. // License. A copy of the License is located at
  8. //
  9. // http://aws.amazon.com/asl/
  10. //
  11. // or in the "license" file accompanying this file. This file is
  12. // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  13. // CONDITIONS OF ANY KIND, express or implied. See the License
  14. // for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. #import "AWSCognitoIdentityProvider.h"
  18. #import "AWSCognitoIdentityUser_Internal.h"
  19. #import "AWSCognitoIdentityUserPool_Internal.h"
  20. #import "AWSCognitoIdentityProviderSrpHelper.h"
  21. #import "AWSJKBigInteger.h"
  22. #import "NSData+AWSCognitoIdentityProvider.h"
  23. #import <CommonCrypto/CommonDigest.h>
  24. @interface AWSCognitoIdentityUserPool()
  25. @property (nonatomic, strong) AWSCognitoIdentityProvider *client;
  26. @end
  27. @implementation AWSCognitoIdentityUser
  28. static const NSString * AWSCognitoIdentityUserDerivedKeyInfo = @"Caldera Derived Key";
  29. static const NSString * AWSCognitoIdentityUserAccessToken = @"accessToken";
  30. static const NSString * AWSCognitoIdentityUserIdToken = @"idToken";
  31. static const NSString * AWSCognitoIdentityUserRefreshToken = @"refreshToken";
  32. static const NSString * AWSCognitoIdentityUserTokenExpiration = @"tokenExpiration";
  33. static const NSString * AWSCognitoIdentityUserDeviceId = @"device.id";
  34. static const NSString * AWSCognitoIdentityUserAsfDeviceId = @"asf.device.id";
  35. static const NSString * AWSCognitoIdentityUserDeviceSecret = @"device.secret";
  36. static const NSString * AWSCognitoIdentityUserDeviceGroup = @"device.group";
  37. static const NSString * AWSCognitoIdentityUserUserAttributePrefix = @"userAttributes.";
  38. -(instancetype) initWithUsername: (NSString *)username pool:(AWSCognitoIdentityUserPool *)pool {
  39. self = [super init];
  40. if(self != nil) {
  41. _username = username;
  42. _pool = pool;
  43. _confirmedStatus = AWSCognitoIdentityUserStatusUnknown;
  44. }
  45. return self;
  46. }
  47. -(AWSTask<AWSCognitoIdentityUserConfirmSignUpResponse *> *) confirmSignUp:(NSString *) confirmationCode {
  48. return [self confirmSignUp:confirmationCode forceAliasCreation:NO];
  49. }
  50. -(AWSTask<AWSCognitoIdentityUserConfirmSignUpResponse *> *) confirmSignUp:(NSString *) confirmationCode forceAliasCreation:(BOOL)forceAliasCreation {
  51. AWSCognitoIdentityProviderConfirmSignUpRequest *request = [AWSCognitoIdentityProviderConfirmSignUpRequest new];
  52. request.clientId = self.pool.userPoolConfiguration.clientId;
  53. request.username = self.username;
  54. request.secretHash = [self.pool calculateSecretHash:self.username];
  55. request.confirmationCode = confirmationCode;
  56. request.forceAliasCreation = (forceAliasCreation?@(YES):@(NO));
  57. request.analyticsMetadata = [self.pool analyticsMetadata];
  58. request.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  59. return [[self.pool.client confirmSignUp:request] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderConfirmSignUpResponse *> * _Nonnull task) {
  60. if (task.error) {
  61. self.confirmedStatus = AWSCognitoIdentityUserStatusUnconfirmed;
  62. return task;
  63. } else {
  64. self.confirmedStatus = AWSCognitoIdentityUserStatusConfirmed;
  65. AWSCognitoIdentityUserConfirmSignUpResponse * response = [AWSCognitoIdentityUserConfirmSignUpResponse new];
  66. [response aws_copyPropertiesFromObject:task.result];
  67. return [AWSTask taskWithResult:response];
  68. }
  69. }];
  70. }
  71. -(AWSTask<AWSCognitoIdentityUserForgotPasswordResponse *> *) forgotPassword {
  72. AWSCognitoIdentityProviderForgotPasswordRequest *request = [AWSCognitoIdentityProviderForgotPasswordRequest new];
  73. request.clientId = self.pool.userPoolConfiguration.clientId;
  74. request.username = self.username;
  75. request.secretHash = [self.pool calculateSecretHash:self.username];
  76. request.analyticsMetadata = [self.pool analyticsMetadata];
  77. request.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  78. return [[self.pool.client forgotPassword:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderForgotPasswordResponse *> * _Nonnull task) {
  79. AWSCognitoIdentityUserForgotPasswordResponse * response = [AWSCognitoIdentityUserForgotPasswordResponse new];
  80. [response aws_copyPropertiesFromObject:task.result];
  81. return [AWSTask taskWithResult:response];
  82. }];
  83. }
  84. -(AWSTask<AWSCognitoIdentityUserConfirmForgotPasswordResponse *> *) confirmForgotPassword: (NSString *)confirmationCode password:(NSString *) password {
  85. AWSCognitoIdentityProviderConfirmForgotPasswordRequest *request = [AWSCognitoIdentityProviderConfirmForgotPasswordRequest new];
  86. request.clientId = self.pool.userPoolConfiguration.clientId;
  87. request.username = self.username;
  88. request.secretHash = [self.pool calculateSecretHash:self.username];
  89. request.password = password;
  90. request.confirmationCode = confirmationCode;
  91. request.analyticsMetadata = [self.pool analyticsMetadata];
  92. request.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  93. return [[self.pool.client confirmForgotPassword:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderConfirmForgotPasswordResponse *> * _Nonnull task) {
  94. AWSCognitoIdentityUserConfirmForgotPasswordResponse * response = [AWSCognitoIdentityUserConfirmForgotPasswordResponse new];
  95. [response aws_copyPropertiesFromObject:task.result];
  96. return [AWSTask taskWithResult:response];
  97. }];
  98. }
  99. -(AWSTask<AWSCognitoIdentityUserResendConfirmationCodeResponse *> *) resendConfirmationCode {
  100. AWSCognitoIdentityProviderResendConfirmationCodeRequest *request = [AWSCognitoIdentityProviderResendConfirmationCodeRequest new];
  101. request.clientId = self.pool.userPoolConfiguration.clientId;
  102. request.username = self.username;
  103. request.secretHash = [self.pool calculateSecretHash:self.username];
  104. request.analyticsMetadata = [self.pool analyticsMetadata];
  105. request.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  106. return [[self.pool.client resendConfirmationCode:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderResendConfirmationCodeResponse *> * _Nonnull task) {
  107. AWSCognitoIdentityUserResendConfirmationCodeResponse * response = [AWSCognitoIdentityUserResendConfirmationCodeResponse new];
  108. [response aws_copyPropertiesFromObject:task.result];
  109. return [AWSTask taskWithResult:response];
  110. }];
  111. }
  112. -(AWSTask<AWSCognitoIdentityUserChangePasswordResponse *>*) changePassword: (NSString*)currentPassword proposedPassword: (NSString *)proposedPassword {
  113. AWSCognitoIdentityProviderChangePasswordRequest* request = [AWSCognitoIdentityProviderChangePasswordRequest new];
  114. request.previousPassword = currentPassword;
  115. request.proposedPassword = proposedPassword;
  116. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  117. request.accessToken = task.result.accessToken.tokenString;
  118. return [[self.pool.client changePassword:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderChangePasswordResponse *> * _Nonnull task) {
  119. AWSCognitoIdentityProviderChangePasswordResponse * apiResponse = task.result;
  120. AWSCognitoIdentityUserChangePasswordResponse *response = [AWSCognitoIdentityUserChangePasswordResponse new];
  121. [response aws_copyPropertiesFromObject:apiResponse];
  122. return [AWSTask taskWithResult:response];
  123. }];
  124. }];
  125. }
  126. -(AWSTask<AWSCognitoIdentityUserGetDetailsResponse *>*) getDetails {
  127. AWSCognitoIdentityProviderGetUserRequest* request = [AWSCognitoIdentityProviderGetUserRequest new];
  128. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  129. request.accessToken = task.result.accessToken.tokenString;
  130. return [[self.pool.client getUser:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderGetUserResponse *> * _Nonnull task) {
  131. AWSCognitoIdentityUserGetDetailsResponse * response = [AWSCognitoIdentityUserGetDetailsResponse new];
  132. [response aws_copyPropertiesFromObject:task.result];
  133. return [AWSTask taskWithResult:response];
  134. }];
  135. }];
  136. }
  137. /**
  138. Get a session
  139. */
  140. -(AWSTask<AWSCognitoIdentityUserSession*> *) getSession {
  141. //check to see if we have valid tokens
  142. __block NSString * keyChainNamespace = [self keyChainNamespaceClientId];
  143. NSString * expirationTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserTokenExpiration];
  144. NSString * expirationDate = self.pool.keychain[expirationTokenKey];
  145. if(expirationDate){
  146. NSDate *expiration = [NSDate aws_dateFromString:expirationDate format:AWSDateISO8601DateFormat1];
  147. NSString * refreshToken = [self refreshTokenFromKeyChain:keyChainNamespace];
  148. // Token exists, the user is confirmed
  149. self.confirmedStatus = AWSCognitoIdentityUserStatusConfirmed;
  150. NSString * accessTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserAccessToken];
  151. NSString * accessToken = self.pool.keychain[accessTokenKey];
  152. //if the session expires > 5 minutes return it and there is at least an accessToken.
  153. if(expiration && [expiration compare:[NSDate dateWithTimeIntervalSinceNow:5 * 60]] == NSOrderedDescending && accessToken){
  154. NSString * idTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserIdToken];
  155. AWSCognitoIdentityUserSession * session = [[AWSCognitoIdentityUserSession alloc] initWithIdToken:self.pool.keychain[idTokenKey] accessToken:accessToken refreshToken:refreshToken expirationTime:expiration];
  156. session.expirationTime = expiration;
  157. return [AWSTask taskWithResult:session];
  158. }
  159. //else refresh it using the refresh token
  160. else if(refreshToken){
  161. AWSCognitoIdentityProviderInitiateAuthRequest * request = [AWSCognitoIdentityProviderInitiateAuthRequest new];
  162. request.authFlow = AWSCognitoIdentityProviderAuthFlowTypeRefreshTokenAuth;
  163. request.clientId = self.pool.userPoolConfiguration.clientId;
  164. request.analyticsMetadata = [self.pool analyticsMetadata];
  165. request.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  166. NSMutableDictionary * authParameters = [[NSMutableDictionary alloc] initWithDictionary:@{@"REFRESH_TOKEN" : refreshToken}];
  167. //refresh token secret hash is actually client secret for this api, set it if it is supplied
  168. if(self.pool.userPoolConfiguration.clientSecret != nil){
  169. [authParameters setObject:self.pool.userPoolConfiguration.clientSecret forKey:@"SECRET_HASH"];
  170. }
  171. [self addDeviceKey:authParameters];
  172. request.authParameters = authParameters;
  173. return [[self.pool.client initiateAuth:request] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse *> * _Nonnull task) {
  174. if(task.error){
  175. //If this token is no longer valid, fall back on interactive auth.
  176. if(task.error.code == AWSCognitoIdentityProviderErrorNotAuthorized) {
  177. return [self interactiveAuth];
  178. } else {
  179. return task;
  180. }
  181. }
  182. AWSCognitoIdentityProviderInitiateAuthResponse *response = task.result;
  183. AWSCognitoIdentityProviderAuthenticationResultType *authResult = response.authenticationResult;
  184. /** Check to see if refreshToken is received in the response.
  185. If not, load it from the keychain.
  186. */
  187. NSString * refreshToken = authResult.refreshToken;
  188. if (refreshToken == nil){
  189. NSString * keyChainNamespace = [self keyChainNamespaceClientId];
  190. refreshToken = [self refreshTokenFromKeyChain:keyChainNamespace];
  191. }
  192. AWSCognitoIdentityUserSession * session = [[AWSCognitoIdentityUserSession alloc] initWithIdToken: authResult.idToken accessToken:authResult.accessToken refreshToken:refreshToken expiresIn:authResult.expiresIn];
  193. [self updateUsernameAndPersistTokens:session];
  194. return [AWSTask taskWithResult:session];
  195. }];
  196. }
  197. }
  198. return [self setConfirmationStatus: [self interactiveAuth]];
  199. }
  200. /**
  201. * Explicitly get a session without using any cached tokens/refresh tokens.
  202. */
  203. - (AWSTask<AWSCognitoIdentityUserSession*>*) getSession:(NSString *)username password:(NSString *)password validationData:(NSArray<AWSCognitoIdentityUserAttributeType*>*)validationData {
  204. AWSTask *authenticationTask = self.pool.userPoolConfiguration.migrationEnabled ? [self migrationAuth:username password:password validationData:validationData lastChallenge:nil] : [self srpAuthInternal:username password:password validationData:validationData lastChallenge:nil isInitialCustomChallenge:NO];
  205. return [self setConfirmationStatus: [authenticationTask continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  206. return [self getSessionInternal:task];
  207. }]];
  208. }
  209. - (AWSTask<AWSCognitoIdentityUserSession*>*) setConfirmationStatus: (AWSTask<AWSCognitoIdentityUserSession*>*) task {
  210. // If the user status is unknown
  211. if (self.confirmedStatus == AWSCognitoIdentityUserStatusUnknown) {
  212. if (task.error) {
  213. if (task.error.code == AWSCognitoIdentityProviderErrorUserNotConfirmed) {
  214. self.confirmedStatus = AWSCognitoIdentityUserStatusUnconfirmed;
  215. }
  216. } else {
  217. self.confirmedStatus = AWSCognitoIdentityUserStatusConfirmed;
  218. }
  219. }
  220. return task;
  221. }
  222. - (AWSTask<AWSCognitoIdentityUserSession*>*) getSessionInternal: (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *>*) task{
  223. return [task continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  224. AWSCognitoIdentityProviderRespondToAuthChallengeResponse * authenticateResult = task.result;
  225. AWSCognitoIdentityProviderAuthenticationResultType * authResult = task.result.authenticationResult;
  226. AWSCognitoIdentityProviderChallengeNameType nextChallenge = authenticateResult.challengeName;
  227. AWSCognitoIdentityUserSession * session = nil;
  228. //No more challenges we have a session
  229. if(authResult != nil){
  230. session = [[AWSCognitoIdentityUserSession alloc] initWithIdToken:authResult.idToken accessToken:authResult.accessToken refreshToken:authResult.refreshToken expiresIn:authResult.expiresIn];
  231. }
  232. //last step is to perform device auth if device key is supplied or we are being challenged with device auth
  233. if(authResult.latestDeviceMetadata != nil || nextChallenge == AWSCognitoIdentityProviderChallengeNameTypeDeviceSrpAuth){
  234. return [self performDeviceAuth: task session:session];
  235. }
  236. //if mfa required, present mfa challenge
  237. if(AWSCognitoIdentityProviderChallengeNameTypeSmsMfa == nextChallenge || AWSCognitoIdentityProviderChallengeNameTypeSoftwareTokenMfa == nextChallenge){
  238. if ([self.pool.delegate respondsToSelector:@selector(startMultiFactorAuthentication)]) {
  239. BOOL isSoftwareToken = AWSCognitoIdentityProviderChallengeNameTypeSoftwareTokenMfa == nextChallenge;
  240. id<AWSCognitoIdentityMultiFactorAuthentication> authenticationDelegate = [self.pool.delegate startMultiFactorAuthentication];
  241. NSString *deliveryMedium = isSoftwareToken ? @"SOFTWARE_TOKEN" : authenticateResult.challengeParameters[@"CODE_DELIVERY_DELIVERY_MEDIUM"];
  242. NSString *destination = isSoftwareToken ? authenticateResult.challengeParameters[@"FRIENDLY_DEVICE_NAME"] : authenticateResult.challengeParameters[@"CODE_DELIVERY_DESTINATION"];
  243. return [self mfaAuthInternal:deliveryMedium destination:destination authState:authenticateResult.session challengeName:nextChallenge authenticationDelegate:authenticationDelegate];
  244. }else {
  245. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"startMultiFactorAuthentication not implemented by authentication delegate"}]];
  246. }
  247. }else if(AWSCognitoIdentityProviderChallengeNameTypeSelectMfaType == nextChallenge){
  248. return [self startSelectMfaUI:authenticateResult];
  249. }else if(AWSCognitoIdentityProviderChallengeNameTypeMfaSetup == nextChallenge){
  250. return [self startMfaSetupRequiredUI:authenticateResult];
  251. }else if(AWSCognitoIdentityProviderChallengeNameTypeNewPasswordRequired == nextChallenge){
  252. return [self startNewPasswordRequiredUI:authenticateResult];
  253. }else if(AWSCognitoIdentityProviderAuthFlowTypeUserSrpAuth == nextChallenge){ //if srp auth happens mid auth
  254. return [self startPasswordAuthenticationUI:authenticateResult];
  255. }else if (session) { //we have a session, return it
  256. [self updateUsernameAndPersistTokens:session];
  257. return [AWSTask taskWithResult:session];
  258. }else { //this is a custom challenge
  259. if ([self.pool.delegate respondsToSelector:@selector(startCustomAuthentication)]) {
  260. id<AWSCognitoIdentityCustomAuthentication> authenticationDelegate = [self.pool.delegate startCustomAuthentication];
  261. AWSCognitoIdentityCustomAuthenticationInput *input = [AWSCognitoIdentityCustomAuthenticationInput new];
  262. input.challengeParameters = authenticateResult.challengeParameters;
  263. AWSTaskCompletionSource<AWSCognitoIdentityCustomChallengeDetails *> *challengeDetails = [AWSTaskCompletionSource<AWSCognitoIdentityCustomChallengeDetails *> new];
  264. [authenticationDelegate getCustomChallengeDetails:input customAuthCompletionSource:challengeDetails];
  265. return [challengeDetails.task continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityCustomChallengeDetails *> * _Nonnull task) {
  266. return [[self performRespondCustomAuthChallenge:task.result session:authenticateResult.session] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  267. [authenticationDelegate didCompleteCustomAuthenticationStepWithError:task.error];
  268. return [self getSessionInternal: task];
  269. }];
  270. }];
  271. }else {
  272. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"startCustomAuthentication not implemented by authentication delegate"}]];
  273. }
  274. }
  275. }];
  276. }
  277. - (AWSTask<AWSCognitoIdentityUserSession*>*) startPasswordAuthenticationUI:(AWSCognitoIdentityProviderRespondToAuthChallengeResponse*) lastChallenge {
  278. if([self.pool.delegate respondsToSelector:@selector(startPasswordAuthentication)]){
  279. id<AWSCognitoIdentityPasswordAuthentication> authenticationDelegate = [self.pool.delegate startPasswordAuthentication];
  280. return [self passwordAuthInternal:authenticationDelegate lastChallenge:lastChallenge isInitialCustomChallenge:lastChallenge == nil];
  281. }else {
  282. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"startPasswordAuthentication must be implemented on your AWSCognitoIdentityInteractiveAuthenticationDelegate"}]];
  283. }
  284. }
  285. - (AWSTask<AWSCognitoIdentityUserSession*>*) startNewPasswordRequiredUI:(AWSCognitoIdentityProviderRespondToAuthChallengeResponse*) lastChallenge {
  286. if ([self.pool.delegate respondsToSelector:@selector(startNewPasswordRequired)]) {
  287. id<AWSCognitoIdentityNewPasswordRequired> newPasswordRequiredDelegate = [self.pool.delegate startNewPasswordRequired];
  288. NSString * userAttributes = lastChallenge.challengeParameters[@"userAttributes"];
  289. NSString * requiredAttributes = lastChallenge.challengeParameters[@"requiredAttributes"];
  290. NSMutableDictionary<NSString*, NSString *> *userAttributesDict = [NSMutableDictionary new];
  291. NSMutableSet<NSString*> *requiredAttributesSet = [NSMutableSet new];
  292. if(userAttributes){
  293. [userAttributesDict addEntriesFromDictionary:[NSJSONSerialization JSONObjectWithData:[userAttributes dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]];
  294. }
  295. if(requiredAttributes) {
  296. NSArray * requiredAttributesArray = [NSJSONSerialization JSONObjectWithData:[requiredAttributes dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
  297. for (NSString * requiredAttribute in requiredAttributesArray) {
  298. //strip off userAttributes. from all the attribute names
  299. NSString *strippedKey = [requiredAttribute substringFromIndex:[AWSCognitoIdentityUserUserAttributePrefix length]];
  300. [requiredAttributesSet addObject:strippedKey];
  301. }
  302. }
  303. AWSCognitoIdentityNewPasswordRequiredInput *newPasswordRequiredInput = [[AWSCognitoIdentityNewPasswordRequiredInput alloc] initWithUserAttributes:userAttributesDict requiredAttributes:requiredAttributesSet];
  304. AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails *> *newPasswordRequiredDetails = [AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails *> new];
  305. [newPasswordRequiredDelegate getNewPasswordDetails:newPasswordRequiredInput newPasswordRequiredCompletionSource:newPasswordRequiredDetails];
  306. return [[newPasswordRequiredDetails.task continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityNewPasswordRequiredDetails *> * _Nonnull task) {
  307. return [self performRespondToNewPasswordChallenge:task.result session:lastChallenge.session];
  308. }] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  309. [newPasswordRequiredDelegate didCompleteNewPasswordStepWithError:task.error];
  310. if(task.error){
  311. return [self startNewPasswordRequiredUI:lastChallenge];
  312. }
  313. return [self getSessionInternal:task];
  314. }];
  315. }else {
  316. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"startNewPasswordRequired not implemented by authentication delegate"}]];
  317. }
  318. }
  319. - (AWSTask<AWSCognitoIdentityUserSession*>*) startMfaSetupRequiredUI:(AWSCognitoIdentityProviderRespondToAuthChallengeResponse*) lastChallenge {
  320. NSString * availableMfas = lastChallenge.challengeParameters[@"MFAS_CAN_SETUP"];
  321. NSMutableSet<NSString*> *availableMfasSet = [NSMutableSet new];
  322. if(availableMfas) {
  323. NSArray * availableMfasArray = [NSJSONSerialization JSONObjectWithData:[availableMfas dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
  324. for (NSString * availableMfa in availableMfasArray) {
  325. [availableMfasSet addObject:availableMfa];
  326. }
  327. }
  328. if([availableMfasSet containsObject:@"SOFTWARE_TOKEN_MFA"]){
  329. if ([self.pool.delegate respondsToSelector:@selector(startSoftwareMfaSetupRequired)]) {
  330. id<AWSCognitoIdentitySoftwareMfaSetupRequired> softwareMfaSetupRequiredDelegate = [self.pool.delegate startSoftwareMfaSetupRequired];
  331. AWSCognitoIdentityProviderAssociateSoftwareTokenRequest *request = [AWSCognitoIdentityProviderAssociateSoftwareTokenRequest new];
  332. request.session = lastChallenge.session;
  333. return [[self.pool.client associateSoftwareToken:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderAssociateSoftwareTokenResponse *> * _Nonnull task) {
  334. AWSCognitoIdentitySoftwareMfaSetupRequiredInput *softwareMfaSetupRequiredInput = [[AWSCognitoIdentitySoftwareMfaSetupRequiredInput alloc] initWithSecretCode:task.result.secretCode username:self.username];
  335. return [self verifyMfaSetup:task.result.session selectMfaDelegate:softwareMfaSetupRequiredDelegate input:softwareMfaSetupRequiredInput];
  336. }];
  337. } else {
  338. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"startSoftwareMfaSetupRequired not implemented by authentication delegate"}]];
  339. }
  340. } else {
  341. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"This version of the SDK does not support setup of the MFA types necessary to authenticate"}]];
  342. }
  343. }
  344. - (AWSTask<AWSCognitoIdentityUserSession*>*) verifyMfaSetup:(NSString *) session selectMfaDelegate:(id<AWSCognitoIdentitySoftwareMfaSetupRequired> ) softwareMfaSetupRequiredDelegate input:(AWSCognitoIdentitySoftwareMfaSetupRequiredInput *) input {
  345. AWSTaskCompletionSource<AWSCognitoIdentitySoftwareMfaSetupRequiredDetails *> *softwareMfaSetupRequiredDetails = [AWSTaskCompletionSource<AWSCognitoIdentitySoftwareMfaSetupRequiredDetails *> new];
  346. [softwareMfaSetupRequiredDelegate getSoftwareMfaSetupDetails:input softwareMfaSetupRequiredCompletionSource:softwareMfaSetupRequiredDetails];
  347. return [[softwareMfaSetupRequiredDetails.task continueWithSuccessBlock:^id _Nullable(AWSTask <AWSCognitoIdentitySoftwareMfaSetupRequiredDetails *>* _Nonnull mfaDetails) {
  348. AWSCognitoIdentityProviderVerifySoftwareTokenRequest *verifyRequest = [AWSCognitoIdentityProviderVerifySoftwareTokenRequest new];
  349. verifyRequest.session = session;
  350. verifyRequest.friendlyDeviceName = mfaDetails.result.friendlyDeviceName;
  351. verifyRequest.userCode = mfaDetails.result.userCode;
  352. return [self.pool.client verifySoftwareToken:verifyRequest];
  353. }] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderVerifySoftwareTokenResponse *> *_Nonnull verifySoftwareToken) {
  354. [softwareMfaSetupRequiredDelegate didCompleteMfaSetupStepWithError:verifySoftwareToken.error];
  355. if(verifySoftwareToken.error){
  356. return [self verifyMfaSetup:session selectMfaDelegate:softwareMfaSetupRequiredDelegate input:input];
  357. }
  358. NSMutableDictionary<NSString *,NSString *> *challengeResponses = [NSMutableDictionary new];
  359. return [self getSessionInternal:[self performRespondToAuthChallenge:challengeResponses challengeName:AWSCognitoIdentityProviderChallengeNameTypeMfaSetup session:verifySoftwareToken.result.session]];
  360. }];
  361. }
  362. - (AWSTask<AWSCognitoIdentityUserSession*>*) startSelectMfaUI:(AWSCognitoIdentityProviderRespondToAuthChallengeResponse*) lastChallenge {
  363. if ([self.pool.delegate respondsToSelector:@selector(startSelectMfa)]) {
  364. id<AWSCognitoIdentitySelectMfa> selectMfaDelegate = [self.pool.delegate startSelectMfa];
  365. NSString * availableMfas = lastChallenge.challengeParameters[@"MFAS_CAN_CHOOSE"];
  366. NSMutableDictionary<NSString*,NSString*> *availableMfasDict = [NSMutableDictionary new];
  367. if(availableMfas) {
  368. NSArray * availableMfasArray = [NSJSONSerialization JSONObjectWithData:[availableMfas dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
  369. for (NSString * availableMfa in availableMfasArray) {
  370. NSString *value = nil;
  371. if([@"SOFTWARE_TOKEN_MFA" isEqualToString:availableMfa]){
  372. value = lastChallenge.challengeParameters[@"FRIENDLY_DEVICE_NAME"];
  373. }else if([@"SMS_MFA" isEqualToString:availableMfa]){
  374. value = lastChallenge.challengeParameters[@"CODE_DELIVERY_DESTINATION"];
  375. }
  376. if(value == nil){
  377. value = @"unknown";
  378. }
  379. [availableMfasDict setObject:value forKey:availableMfa];
  380. }
  381. }
  382. AWSCognitoIdentitySelectMfaInput *selectMfaInput = [[AWSCognitoIdentitySelectMfaInput alloc] initWithAvailableMfas:availableMfasDict];
  383. AWSTaskCompletionSource<AWSCognitoIdentitySelectMfaDetails *> *selectMfaDetails = [AWSTaskCompletionSource<AWSCognitoIdentitySelectMfaDetails *> new];
  384. [selectMfaDelegate getSelectMfaDetails:selectMfaInput selectMfaCompletionSource:selectMfaDetails];
  385. return [[selectMfaDetails.task continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentitySelectMfaDetails *> * _Nonnull task) {
  386. return [self performRespondToSelectMfaChallenge:task.result session:lastChallenge.session];
  387. }] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  388. [selectMfaDelegate didCompleteSelectMfaStepWithError:task.error];
  389. if(task.error){
  390. return [self startSelectMfaUI:lastChallenge];
  391. }
  392. return [self getSessionInternal:task];
  393. }];
  394. }else {
  395. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"startSelectMfa not implemented by authentication delegate"}]];
  396. }
  397. }
  398. - (AWSTask<AWSCognitoIdentityUserSession*>*) performDeviceAuth:(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *>*) lastChallengeResponse session: (AWSCognitoIdentityUserSession *) session {
  399. if(session){
  400. [self updateUsernameAndPersistTokens:session];
  401. }
  402. if(lastChallengeResponse.result.challengeName == AWSCognitoIdentityProviderChallengeNameTypeDeviceSrpAuth){
  403. return [[self deviceAuthInternal:lastChallengeResponse] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  404. if(task.cancelled || task.error) {
  405. return task;
  406. }else {
  407. AWSCognitoIdentityProviderRespondToAuthChallengeResponse *response = task.result;
  408. AWSCognitoIdentityUserSession * session = [[AWSCognitoIdentityUserSession alloc] initWithIdToken:response.authenticationResult.idToken accessToken:response.authenticationResult.accessToken refreshToken:response.authenticationResult.refreshToken expiresIn:response.authenticationResult.expiresIn];
  409. [self updateUsernameAndPersistTokens:session];
  410. return [AWSTask taskWithResult:session];
  411. }
  412. }];
  413. }else {
  414. return [[self confirmDeviceInternal:lastChallengeResponse.result.authenticationResult] continueWithBlock:^id _Nullable(AWSTask * _Nonnull task) {
  415. if(task.error || task.isCancelled){
  416. return task;
  417. }else {
  418. return [AWSTask taskWithResult:session];
  419. }
  420. }];
  421. }
  422. }
  423. /**
  424. * Generates a device password, calls service to exchange the password verifier and prompts user to remember the device as required.
  425. */
  426. - (AWSTask*) confirmDeviceInternal:(AWSCognitoIdentityProviderAuthenticationResultType *) authResult {
  427. if(authResult.latestDeviceMetadata != nil){
  428. NSString * deviceKey = authResult.latestDeviceMetadata.deviceKey;
  429. NSString * deviceGroup = authResult.latestDeviceMetadata.deviceGroupKey;
  430. if(deviceKey != nil){
  431. NSString *secret = [[NSUUID UUID] UUIDString];
  432. AWSCognitoIdentityProviderConfirmDeviceRequest * request = [AWSCognitoIdentityProviderConfirmDeviceRequest new];
  433. request.accessToken = authResult.accessToken;
  434. request.deviceKey = deviceKey;
  435. request.deviceName = [[UIDevice currentDevice] name];
  436. AWSCognitoIdentityProviderSrpHelper * srpHelper = [[AWSCognitoIdentityProviderSrpHelper alloc] initWithPoolName:deviceGroup userName:deviceKey password:secret];
  437. request.deviceSecretVerifierConfig = [AWSCognitoIdentityProviderDeviceSecretVerifierConfigType new];
  438. request.deviceSecretVerifierConfig.salt = [[NSData aws_dataWithSignedBigInteger:srpHelper.salt] base64EncodedStringWithOptions:0];
  439. request.deviceSecretVerifierConfig.passwordVerifier = [[NSData aws_dataWithSignedBigInteger:srpHelper.v] base64EncodedStringWithOptions:0];
  440. return [[self.pool.client confirmDevice:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderConfirmDeviceResponse *> * _Nonnull task) {
  441. [self persistDevice:deviceKey deviceSecret:secret deviceGroup:deviceGroup];
  442. AWSCognitoIdentityProviderConfirmDeviceResponse *confirmDeviceResponse = task.result;
  443. if([confirmDeviceResponse.userConfirmationNecessary boolValue]) {
  444. if ([self.pool.delegate respondsToSelector:@selector(startRememberDevice)]) {
  445. id<AWSCognitoIdentityRememberDevice> rememberDeviceStep = [self.pool.delegate startRememberDevice];
  446. AWSTaskCompletionSource<NSNumber *> *rememberDevice = [[AWSTaskCompletionSource<NSNumber *> alloc] init];
  447. [rememberDeviceStep getRememberDevice:rememberDevice];
  448. return [rememberDevice.task continueWithBlock:^id _Nullable(AWSTask<NSNumber *> * _Nonnull rememberDeviceTask) {
  449. if(rememberDeviceTask.isCancelled || rememberDeviceTask.error){
  450. [rememberDeviceStep didCompleteRememberDeviceStepWithError:rememberDeviceTask.error];
  451. return rememberDeviceStep;
  452. }else if ([rememberDeviceTask.result boolValue]){
  453. AWSCognitoIdentityProviderUpdateDeviceStatusRequest * request = [AWSCognitoIdentityProviderUpdateDeviceStatusRequest new];
  454. request.accessToken = authResult.accessToken;
  455. request.deviceKey = deviceKey;
  456. request.deviceRememberedStatus = AWSCognitoIdentityProviderDeviceRememberedStatusTypeRemembered;
  457. return [[self.pool.client updateDeviceStatus:request] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderUpdateDeviceStatusResponse *> * _Nonnull updateDeviceStatusTask) {
  458. [rememberDeviceStep didCompleteRememberDeviceStepWithError:rememberDeviceTask.error];
  459. return updateDeviceStatusTask;
  460. }];
  461. }
  462. return task;
  463. }];
  464. }else {
  465. AWSDDLogWarn(@"startRememberDevice is not implemented by authentication delegate, defaulting to not remembered.");
  466. }
  467. }
  468. return task;
  469. }];
  470. }
  471. }
  472. return [AWSTask taskWithResult:nil];
  473. }
  474. /**
  475. * Kick off interactive auth to prompt developer to challenge end user for credentials
  476. */
  477. - (AWSTask<AWSCognitoIdentityUserSession*>*) interactiveAuth {
  478. if(self.pool.delegate != nil){
  479. if([self.pool.delegate respondsToSelector:@selector(startCustomAuthentication)] && !self.pool.userPoolConfiguration.migrationEnabled) {
  480. id<AWSCognitoIdentityCustomAuthentication> authenticationDelegate = [self.pool.delegate startCustomAuthentication];
  481. return [self customAuthInternal:authenticationDelegate];
  482. }else if([self.pool.delegate respondsToSelector:@selector(startPasswordAuthentication)]){
  483. id<AWSCognitoIdentityPasswordAuthentication> authenticationDelegate = [self.pool.delegate startPasswordAuthentication];
  484. return [self passwordAuthInternal:authenticationDelegate lastChallenge:nil isInitialCustomChallenge:NO];
  485. }else {
  486. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"Either startCustomAuthentication or startPasswordAuthentication must be implemented on your AWSCognitoIdentityInteractiveAuthenticationDelegate"}]];
  487. }
  488. } else {
  489. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorInvalidAuthenticationDelegate userInfo:@{NSLocalizedDescriptionKey: @"Authentication delegate not set"}]];
  490. };
  491. }
  492. /**
  493. * Prompt developer to obtain username/password and do SRP auth
  494. */
  495. - (AWSTask<AWSCognitoIdentityUserSession*>*) passwordAuthInternal: (id<AWSCognitoIdentityPasswordAuthentication>) authenticationDelegate lastChallenge:(AWSCognitoIdentityProviderRespondToAuthChallengeResponse*) lastChallenge isInitialCustomChallenge:(BOOL) isInitialCustomChallenge {
  496. AWSCognitoIdentityPasswordAuthenticationInput * input = [[AWSCognitoIdentityPasswordAuthenticationInput alloc] initWithLastKnownUsername:[self.pool currentUsername]];
  497. AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails*>*passwordAuthenticationDetails = [AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails*> new];
  498. [authenticationDelegate getPasswordAuthenticationDetails:input passwordAuthenticationCompletionSource:passwordAuthenticationDetails];
  499. return [passwordAuthenticationDetails.task continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityPasswordAuthenticationDetails *> * _Nonnull task) {
  500. AWSCognitoIdentityPasswordAuthenticationDetails * authDetails = task.result;
  501. if(self.pool.userPoolConfiguration.migrationEnabled){
  502. return [[self migrationAuth:authDetails.username password:authDetails.password validationData:authDetails.validationData lastChallenge:lastChallenge] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  503. [authenticationDelegate didCompletePasswordAuthenticationStepWithError:task.error];
  504. if(task.isCancelled){
  505. return task;
  506. }
  507. if(task.error){
  508. //retry password auth on error
  509. return [self passwordAuthInternal:authenticationDelegate lastChallenge:lastChallenge isInitialCustomChallenge:NO];
  510. }else {
  511. //morph this initiate auth response into a respond to auth challenge response so it works as input to getSessionInternal
  512. AWSCognitoIdentityProviderRespondToAuthChallengeResponse * response = [AWSCognitoIdentityProviderRespondToAuthChallengeResponse new];
  513. [response aws_copyPropertiesFromObject:task.result];
  514. return [self getSessionInternal:[AWSTask taskWithResult:response]];
  515. }
  516. }];
  517. } else {
  518. return [[self srpAuthInternal:authDetails.username password:authDetails.password validationData:authDetails.validationData lastChallenge:lastChallenge isInitialCustomChallenge:isInitialCustomChallenge] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  519. [authenticationDelegate didCompletePasswordAuthenticationStepWithError:task.error];
  520. if(task.isCancelled){
  521. return task;
  522. }
  523. if(task.error){
  524. //retry password auth on error
  525. return [self passwordAuthInternal:authenticationDelegate lastChallenge:lastChallenge isInitialCustomChallenge:isInitialCustomChallenge];
  526. }else {
  527. return [self getSessionInternal:task];
  528. }
  529. }];
  530. }
  531. }];
  532. }
  533. /**
  534. * Pass username and password in plaintext so developer can validate login and migrate as appropriate.
  535. **/
  536. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) migrationAuth:(NSString *)username password:(NSString *)password validationData:(NSArray<AWSCognitoIdentityUserAttributeType*>*)validationData lastChallenge:(AWSCognitoIdentityProviderRespondToAuthChallengeResponse*) lastChallenge {
  537. self.username = username;
  538. NSMutableDictionary *challengeResponses = [NSMutableDictionary new];
  539. [self addSecretHashDeviceKeyAndUsername:challengeResponses];
  540. [challengeResponses setObject:password forKey:@"PASSWORD"];
  541. if(lastChallenge){
  542. AWSCognitoIdentityProviderRespondToAuthChallengeRequest *input = [AWSCognitoIdentityProviderRespondToAuthChallengeRequest new];
  543. input.challengeName = lastChallenge.challengeName;
  544. input.challengeResponses = challengeResponses;
  545. input.session = lastChallenge.session;
  546. input.analyticsMetadata = [self.pool analyticsMetadata];
  547. input.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  548. return [[self.pool.client respondToAuthChallenge:input] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  549. return [self forgetDeviceOnRespondDeviceNotFoundError:task retryContinuation:^AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> *{
  550. return [self migrationAuth:username password:password validationData:validationData lastChallenge:lastChallenge];
  551. }];
  552. }];
  553. }
  554. else{
  555. AWSCognitoIdentityProviderInitiateAuthRequest *input = [AWSCognitoIdentityProviderInitiateAuthRequest new];
  556. input.clientId = self.pool.userPoolConfiguration.clientId;
  557. input.clientMetadata = [self.pool getValidationData:validationData];
  558. input.analyticsMetadata = [self.pool analyticsMetadata];
  559. input.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  560. input.authFlow = AWSCognitoIdentityProviderAuthFlowTypeUserPasswordAuth;
  561. input.authParameters = challengeResponses;
  562. return [[self.pool.client initiateAuth:input] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse *> * _Nonnull task) {
  563. //if there was an error, it may be due to the device being forgotten, reset the device and retry if that is the case
  564. return [self forgetDeviceOnInitiateDeviceNotFoundError:task retryContinuation:^AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> *{
  565. return [self migrationAuth:username password:password validationData:validationData lastChallenge:lastChallenge];
  566. }];
  567. }];
  568. }
  569. }
  570. /**
  571. * Prompt developer to obtain custom challenge details
  572. */
  573. - (AWSTask<AWSCognitoIdentityUserSession*>*) customAuthInternal: (id<AWSCognitoIdentityCustomAuthentication>) authenticationDelegate {
  574. AWSTaskCompletionSource<AWSCognitoIdentityCustomChallengeDetails *> *customAuthenticationDetails = [AWSTaskCompletionSource<AWSCognitoIdentityCustomChallengeDetails *> new];
  575. AWSCognitoIdentityCustomAuthenticationInput *input = [[AWSCognitoIdentityCustomAuthenticationInput alloc] initWithChallengeParameters: [NSDictionary new]];
  576. [authenticationDelegate getCustomChallengeDetails:input customAuthCompletionSource:customAuthenticationDetails];
  577. return [[customAuthenticationDetails.task continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityCustomChallengeDetails *> * _Nonnull task) {
  578. //if first challenge is SRP auth
  579. if([self isFirstCustomStepSRP:task.result]){
  580. return [self startPasswordAuthenticationUI:nil];
  581. }else {
  582. return [[self performInitiateCustomAuthChallenge:task.result]
  583. continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse *> * _Nonnull task) {
  584. [authenticationDelegate didCompleteCustomAuthenticationStepWithError:task.error];
  585. if(task.isCancelled){
  586. return task;
  587. }
  588. if(task.error){
  589. //retry auth on error
  590. return [self customAuthInternal:authenticationDelegate];
  591. }else {
  592. //morph this initiate auth response into a respond to auth challenge response so it works as input to getSessionInternal
  593. AWSCognitoIdentityProviderRespondToAuthChallengeResponse * response = [AWSCognitoIdentityProviderRespondToAuthChallengeResponse new];
  594. [response aws_copyPropertiesFromObject:task.result];
  595. return [self getSessionInternal:[AWSTask taskWithResult:response]];
  596. }
  597. }];
  598. }
  599. }] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  600. if(task.error){
  601. //retry auth on error
  602. return [self customAuthInternal:authenticationDelegate];
  603. }else {
  604. return task;
  605. }
  606. }];
  607. }
  608. - (BOOL) isFirstCustomStepSRP: (AWSCognitoIdentityCustomChallengeDetails *) customAuthenticationDetails {
  609. return customAuthenticationDetails.initialChallengeName != nil && [@"SRP_A" isEqualToString: customAuthenticationDetails.initialChallengeName];
  610. }
  611. /**
  612. * Run initiate auth on challenge responses from end user for custom auth
  613. */
  614. - (AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse*>*) performInitiateCustomAuthChallenge: (AWSCognitoIdentityCustomChallengeDetails *) challengeDetails {
  615. AWSCognitoIdentityProviderInitiateAuthRequest *input = [AWSCognitoIdentityProviderInitiateAuthRequest new];
  616. input.clientId = self.pool.userPoolConfiguration.clientId;
  617. input.clientMetadata = [self.pool getValidationData:challengeDetails.validationData];
  618. input.analyticsMetadata = [self.pool analyticsMetadata];
  619. input.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  620. NSMutableDictionary * authParameters = [[NSMutableDictionary alloc] initWithDictionary:challengeDetails.challengeResponses];
  621. [self addSecretHashDeviceKeyAndUsername:authParameters];
  622. if(challengeDetails.initialChallengeName != nil){
  623. [authParameters setObject:challengeDetails.initialChallengeName forKey:@"CHALLENGE_NAME"];
  624. }
  625. input.authFlow = AWSCognitoIdentityProviderAuthFlowTypeCustomAuth;
  626. input.authParameters = authParameters;
  627. return [[self.pool.client initiateAuth:input] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse *> * _Nonnull task) {
  628. //if there was an error, it may be due to the device being forgotten, reset the device and retry if that is the case
  629. return [self forgetDeviceOnInitiateDeviceNotFoundError:task retryContinuation:^AWSTask *{
  630. return [self performInitiateCustomAuthChallenge:challengeDetails];
  631. }];
  632. }];
  633. }
  634. /**
  635. * Run respond to auth challenges on challenge responses from end user for custom auth
  636. */
  637. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) performRespondCustomAuthChallenge: (AWSCognitoIdentityCustomChallengeDetails *) challengeDetails session: (NSString *) session{
  638. NSMutableDictionary<NSString *,NSString *> *challengeResponses = [NSMutableDictionary new];
  639. [challengeResponses addEntriesFromDictionary:challengeDetails.challengeResponses];
  640. if([challengeResponses objectForKey:@"USERNAME"] != nil){
  641. self.username = [challengeResponses objectForKey:@"USERNAME"];
  642. }
  643. return [[self performRespondToAuthChallenge:challengeResponses challengeName:AWSCognitoIdentityProviderChallengeNameTypeCustomChallenge session:session] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  644. //if there was an error, it may be due to the device being forgotten, reset the device and retry if that is the case
  645. return [self forgetDeviceOnRespondDeviceNotFoundError:task retryContinuation:^AWSTask *{
  646. return [self performRespondCustomAuthChallenge:challengeDetails session:session];
  647. }];
  648. }];
  649. }
  650. /**
  651. * Run respond to auth challenges on new password required responses from end user
  652. */
  653. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) performRespondToNewPasswordChallenge: (AWSCognitoIdentityNewPasswordRequiredDetails *) details session: (NSString *) session{
  654. NSMutableDictionary<NSString *,NSString *> *challengeResponses = [NSMutableDictionary new];
  655. [challengeResponses setObject:details.proposedPassword forKey:@"NEW_PASSWORD"];
  656. for(AWSCognitoIdentityUserAttributeType *userAttribute in details.userAttributes){
  657. [challengeResponses setObject:userAttribute.value forKey: [NSString stringWithFormat:@"%@%@", AWSCognitoIdentityUserUserAttributePrefix, userAttribute.name]];
  658. }
  659. return [self performRespondToAuthChallenge:challengeResponses challengeName:AWSCognitoIdentityProviderChallengeNameTypeNewPasswordRequired session:session];
  660. }
  661. /**
  662. * Run respond to auth challenges on select mfa responses from end user
  663. */
  664. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) performRespondToSelectMfaChallenge: (AWSCognitoIdentitySelectMfaDetails *) details session: (NSString *) session{
  665. NSMutableDictionary<NSString *,NSString *> *challengeResponses = [NSMutableDictionary new];
  666. [challengeResponses setObject:details.selectedMfa forKey:@"ANSWER"];
  667. return [self performRespondToAuthChallenge:challengeResponses challengeName:AWSCognitoIdentityProviderChallengeNameTypeSelectMfaType session:session];
  668. }
  669. /**
  670. * Run respond to auth challenges
  671. */
  672. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) performRespondToAuthChallenge: (NSMutableDictionary *) challengeResponses challengeName: (AWSCognitoIdentityProviderChallengeNameType) challengeName session: (NSString *) session{
  673. AWSCognitoIdentityProviderRespondToAuthChallengeRequest *request = [AWSCognitoIdentityProviderRespondToAuthChallengeRequest new];
  674. request.session = session;
  675. request.clientId = self.pool.userPoolConfiguration.clientId;
  676. request.challengeName = challengeName;
  677. request.analyticsMetadata = [self.pool analyticsMetadata];
  678. request.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  679. [self addSecretHashDeviceKeyAndUsername:challengeResponses];
  680. request.challengeResponses = challengeResponses;
  681. return [self.pool.client respondToAuthChallenge:request];
  682. }
  683. /**
  684. * Perform SRP based authentication (initiateAuth(SRP_AUTH) and respondToAuthChallenge) given a username and password. If lastChallenge is supplied it starts with respondToAuthChallenge instead of initiate.
  685. */
  686. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) srpAuthInternal:(NSString *)username password:(NSString *)password validationData:(NSArray<AWSCognitoIdentityUserAttributeType*>*)validationData lastChallenge:(AWSCognitoIdentityProviderRespondToAuthChallengeResponse*) lastChallenge isInitialCustomChallenge:(BOOL) isInitialCustomChallenge {
  687. self.username = username;
  688. AWSCognitoIdentityProviderSrpHelper *srpHelper = [AWSCognitoIdentityProviderSrpHelper beginUserAuthentication:self.username password:password];
  689. NSMutableDictionary * challengeResponses = [[NSMutableDictionary alloc] initWithDictionary:@{@"SRP_A" : [srpHelper.clientState.publicA stringValueWithRadix:16]}];
  690. [self addSecretHashDeviceKeyAndUsername:challengeResponses];
  691. if(lastChallenge){
  692. AWSCognitoIdentityProviderRespondToAuthChallengeRequest *input = [AWSCognitoIdentityProviderRespondToAuthChallengeRequest new];
  693. input.clientId = self.pool.userPoolConfiguration.clientId;
  694. input.challengeName = lastChallenge.challengeName;
  695. input.challengeResponses = challengeResponses;
  696. input.session = lastChallenge.session;
  697. input.analyticsMetadata = [self.pool analyticsMetadata];
  698. input.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  699. return [[self.pool.client respondToAuthChallenge:input] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  700. //if there was an error, it may be due to the device being forgotten, reset the device and retry if that is the case
  701. return [[self forgetDeviceOnRespondDeviceNotFoundError:task retryContinuation:^AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> *{
  702. return [self srpAuthInternal:username password:password validationData:validationData lastChallenge:lastChallenge isInitialCustomChallenge:isInitialCustomChallenge];
  703. }] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse *> * _Nonnull task) {
  704. //morph this initiate auth response into a respond to auth challenge response so it works as input to srpAuthInternalStep2
  705. AWSCognitoIdentityProviderRespondToAuthChallengeResponse * response = [AWSCognitoIdentityProviderRespondToAuthChallengeResponse new];
  706. [response aws_copyPropertiesFromObject:task.result];
  707. //continue with second step of SRP auth
  708. return [self srpAuthInternalStep2:[AWSTask taskWithResult:response] srpHelper:srpHelper];
  709. }];
  710. }];
  711. }else{
  712. AWSCognitoIdentityProviderInitiateAuthRequest *input = [AWSCognitoIdentityProviderInitiateAuthRequest new];
  713. input.clientId = self.pool.userPoolConfiguration.clientId;
  714. input.clientMetadata = [self.pool getValidationData:validationData];
  715. input.analyticsMetadata = [self.pool analyticsMetadata];
  716. input.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  717. //based on whether this is custom auth or not set the auth flow
  718. if(isInitialCustomChallenge){
  719. input.authFlow = AWSCognitoIdentityProviderAuthFlowTypeCustomAuth;
  720. //set challenge name parameter to SRP_A
  721. [challengeResponses setObject:@"SRP_A" forKey:@"CHALLENGE_NAME"];
  722. }else {
  723. input.authFlow = AWSCognitoIdentityProviderAuthFlowTypeUserSrpAuth;
  724. }
  725. input.authParameters = challengeResponses;
  726. return [[self.pool.client initiateAuth:input] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse *> * _Nonnull task) {
  727. //if there was an error, it may be due to the device being forgotten, reset the device and retry if that is the case
  728. return [[self forgetDeviceOnInitiateDeviceNotFoundError:task retryContinuation:^AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> *{
  729. return [self srpAuthInternal:username password:password validationData:validationData lastChallenge:lastChallenge isInitialCustomChallenge:isInitialCustomChallenge];
  730. }] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse *> * _Nonnull task) {
  731. //morph this initiate auth response into a respond to auth challenge response so it works as input to getSessionInternal
  732. AWSCognitoIdentityProviderRespondToAuthChallengeResponse * response = [AWSCognitoIdentityProviderRespondToAuthChallengeResponse new];
  733. [response aws_copyPropertiesFromObject:task.result];
  734. //continue with second step of SRP auth
  735. return [self srpAuthInternalStep2:[AWSTask taskWithResult:response] srpHelper:srpHelper];
  736. }];
  737. }];
  738. }
  739. }
  740. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> *) srpAuthInternalStep2: (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> *) task srpHelper:(AWSCognitoIdentityProviderSrpHelper *) srpHelper {
  741. AWSCognitoIdentityProviderRespondToAuthChallengeResponse *authDetails = task.result;
  742. AWSCognitoIdentityProviderSrpServerState *serverState =
  743. [AWSCognitoIdentityProviderSrpServerState
  744. serverStateForPoolName:[self.pool strippedPoolId]
  745. publicBHexString:authDetails.challengeParameters[@"SRP_B"]
  746. saltHexString:authDetails.challengeParameters[@"SALT"]
  747. derivedKeyInfo:AWSCognitoIdentityUserDerivedKeyInfo
  748. derivedKeySize:16
  749. serviceSecretBlock:[[NSData alloc] initWithBase64EncodedString:authDetails.challengeParameters[@"SECRET_BLOCK"] options:0]];
  750. self.username = authDetails.challengeParameters[@"USERNAME"];
  751. self.userIdForSRP = authDetails.challengeParameters[@"USER_ID_FOR_SRP"];
  752. srpHelper.clientState.userName = self.userIdForSRP;
  753. AWSCognitoIdentityProviderRespondToAuthChallengeRequest *authInput = [AWSCognitoIdentityProviderRespondToAuthChallengeRequest new];
  754. authInput.session = authDetails.session;
  755. authInput.challengeName = authDetails.challengeName;
  756. authInput.analyticsMetadata = [self.pool analyticsMetadata];
  757. authInput.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  758. NSMutableDictionary * authParameters = [[NSMutableDictionary alloc] initWithDictionary:@{@"TIMESTAMP": [AWSCognitoIdentityProviderSrpHelper generateDateString:srpHelper.clientState.timestamp],
  759. @"PASSWORD_CLAIM_SECRET_BLOCK" : authDetails.challengeParameters[@"SECRET_BLOCK"],
  760. @"PASSWORD_CLAIM_SIGNATURE" : [[srpHelper completeAuthentication:serverState] base64EncodedStringWithOptions:0]
  761. }];
  762. [self addSecretHashDeviceKeyAndUsername:authParameters];
  763. authInput.challengeResponses = authParameters;
  764. authInput.clientId = self.pool.userPoolConfiguration.clientId;
  765. return [[self.pool.client respondToAuthChallenge:authInput] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull responseTask) {
  766. return [self forgetDeviceOnRespondDeviceNotFoundError:responseTask retryContinuation:^AWSTask *{
  767. return [self srpAuthInternalStep2:task srpHelper:srpHelper];
  768. }];
  769. }];
  770. }
  771. //Determine whether the error (if any) on the initiateAuth is a device not found error and if so forget the device and retry
  772. - (AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse*>*) forgetDeviceOnInitiateDeviceNotFoundError:(AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse*> * _Nonnull) task retryContinuation: (AWSTask<AWSCognitoIdentityProviderInitiateAuthResponse*>* (^_Nonnull)(void)) retryContinuation {
  773. if([self isDeviceNotFoundError:task.error]){
  774. [self forgetDeviceInternal];
  775. return retryContinuation();
  776. }
  777. return task;
  778. }
  779. //Determine whether the error (if any) on the respondToAuthChallenge is a device not found error and if so forget the device and retry
  780. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) forgetDeviceOnRespondDeviceNotFoundError:(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*> * _Nonnull) task retryContinuation: (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>* (^_Nonnull)(void)) retryContinuation {
  781. if([self isDeviceNotFoundError:task.error]){
  782. [self forgetDeviceInternal];
  783. return retryContinuation();
  784. }
  785. return task;
  786. }
  787. - (BOOL) isDeviceNotFoundError: (NSError *) error {
  788. return error != nil && error.code == AWSCognitoIdentityProviderErrorResourceNotFound && [@"Device does not exist." isEqualToString:error.userInfo[@"message"]];
  789. }
  790. /**
  791. * Perform SRP based authentication (initiateAuth(SRP_AUTH) and respondToAuthChallenge) given a username and password
  792. */
  793. - (AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse*>*) deviceAuthInternal:(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *>*) deviceChallengeResponse {
  794. AWSCognitoIdentityProviderRespondToAuthChallengeRequest *input = [AWSCognitoIdentityProviderRespondToAuthChallengeRequest new];
  795. input.clientId = self.pool.userPoolConfiguration.clientId;
  796. AWSCognitoIdentityUserDeviceCredentials *deviceCredentials = [self getDeviceCredentials];
  797. AWSCognitoIdentityProviderRespondToAuthChallengeResponse * deviceChallenge = deviceChallengeResponse.result;
  798. AWSCognitoIdentityProviderSrpHelper *srpHelper = [AWSCognitoIdentityProviderSrpHelper beginUserAuthentication:deviceCredentials.deviceId password:deviceCredentials.deviceSecret];
  799. NSMutableDictionary * challengeResponses = [[NSMutableDictionary alloc] initWithDictionary:@{@"SRP_A" : [srpHelper.clientState.publicA stringValueWithRadix:16]}];
  800. [self addSecretHashDeviceKeyAndUsername:challengeResponses];
  801. input.challengeName = deviceChallenge.challengeName;
  802. input.session = deviceChallenge.session;
  803. input.challengeResponses = challengeResponses;
  804. input.analyticsMetadata = [self.pool analyticsMetadata];
  805. input.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  806. return [[self.pool.client respondToAuthChallenge:input] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  807. AWSCognitoIdentityProviderRespondToAuthChallengeResponse *authDetails = task.result;
  808. AWSCognitoIdentityProviderSrpServerState *serverState =
  809. [AWSCognitoIdentityProviderSrpServerState
  810. serverStateForPoolName:deviceCredentials.deviceGroup
  811. publicBHexString:authDetails.challengeParameters[@"SRP_B"]
  812. saltHexString:authDetails.challengeParameters[@"SALT"]
  813. derivedKeyInfo:AWSCognitoIdentityUserDerivedKeyInfo
  814. derivedKeySize:16
  815. serviceSecretBlock:[[NSData alloc] initWithBase64EncodedString:authDetails.challengeParameters[@"SECRET_BLOCK"] options:0]];
  816. AWSCognitoIdentityProviderRespondToAuthChallengeRequest *authInput = [AWSCognitoIdentityProviderRespondToAuthChallengeRequest new];
  817. authInput.session = authDetails.session;
  818. authInput.challengeName = authDetails.challengeName;
  819. authInput.analyticsMetadata = [self.pool analyticsMetadata];
  820. authInput.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  821. NSMutableDictionary * authParameters = [[NSMutableDictionary alloc] initWithDictionary:@{@"TIMESTAMP": [AWSCognitoIdentityProviderSrpHelper generateDateString:srpHelper.clientState.timestamp],
  822. @"PASSWORD_CLAIM_SECRET_BLOCK" : authDetails.challengeParameters[@"SECRET_BLOCK"],
  823. @"PASSWORD_CLAIM_SIGNATURE" : [[srpHelper completeAuthentication:serverState] base64EncodedStringWithOptions:0]
  824. }];
  825. [self addSecretHashDeviceKeyAndUsername:authParameters];
  826. authInput.challengeResponses = authParameters;
  827. authInput.clientId = self.pool.userPoolConfiguration.clientId;
  828. return [self.pool.client respondToAuthChallenge:authInput];
  829. }];
  830. }
  831. /**
  832. * Adds a device key to the authParameters dictionary if it is known for this username
  833. */
  834. -(void) addDeviceKey:(NSMutableDictionary<NSString *,NSString*> *) authParameters {
  835. AWSCognitoIdentityUserDeviceCredentials *deviceCredentials = [self getDeviceCredentials];
  836. if(deviceCredentials != nil){
  837. [authParameters setObject:deviceCredentials.deviceId forKey:@"DEVICE_KEY"];
  838. }
  839. }
  840. /**
  841. * Adds a SECRET_HASH and USERNAME and DEVICE_KEY to the authParameters dictionary.
  842. */
  843. -(void) addSecretHashDeviceKeyAndUsername:(NSMutableDictionary<NSString *,NSString*> *) authParameters {
  844. if(self.username == nil) {
  845. self.username = authParameters[@"USERNAME"];
  846. }
  847. [authParameters setObject:self.username forKey:@"USERNAME"];
  848. NSString* secretHash = [self.pool calculateSecretHash:self.username];
  849. if(secretHash != nil){
  850. [authParameters setObject:secretHash forKey:@"SECRET_HASH"];
  851. }
  852. [self addDeviceKey:authParameters];
  853. }
  854. /**
  855. * Invoke developer's ui to prompt user for mfa code and call enhanceAuth
  856. */
  857. -(AWSTask<AWSCognitoIdentityUserSession *>*) mfaAuthInternal: (NSString *) deliveryMedium destination:(NSString *) destination authState:(NSString *) authState challengeName: (AWSCognitoIdentityProviderChallengeNameType) challengeName authenticationDelegate:(id<AWSCognitoIdentityMultiFactorAuthentication>)authenticationDelegate{
  858. AWSTaskCompletionSource<NSString *> *mfaCode = [[AWSTaskCompletionSource<NSString *> alloc] init];
  859. AWSCognitoIdentityMultifactorAuthenticationInput* authenticationInput = [[AWSCognitoIdentityMultifactorAuthenticationInput alloc] initWithDeliveryMedium:deliveryMedium destination:destination];
  860. [authenticationDelegate getMultiFactorAuthenticationCode:authenticationInput mfaCodeCompletionSource:mfaCode];
  861. return [mfaCode.task
  862. continueWithSuccessBlock:^id _Nullable(AWSTask<NSString *> * _Nonnull task) {
  863. AWSCognitoIdentityProviderRespondToAuthChallengeRequest *mfaChallenge = [AWSCognitoIdentityProviderRespondToAuthChallengeRequest new];
  864. mfaChallenge.session = authState;
  865. mfaChallenge.challengeName = challengeName;
  866. mfaChallenge.clientId = self.pool.userPoolConfiguration.clientId;
  867. NSString * responseKey = @"SMS_MFA_CODE";
  868. if(AWSCognitoIdentityProviderChallengeNameTypeSoftwareTokenMfa == challengeName){
  869. responseKey = @"SOFTWARE_TOKEN_MFA_CODE";
  870. }
  871. NSMutableDictionary * challengeResponses = [[NSMutableDictionary alloc] initWithDictionary:@{responseKey: mfaCode.task.result}];
  872. [self addSecretHashDeviceKeyAndUsername:challengeResponses];
  873. mfaChallenge.challengeResponses = challengeResponses;
  874. mfaChallenge.analyticsMetadata = [self.pool analyticsMetadata];
  875. mfaChallenge.userContextData = [self.pool userContextData:self.username deviceId: [self asfDeviceId]];
  876. return [[[self.pool.client respondToAuthChallenge:mfaChallenge] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderRespondToAuthChallengeResponse *> * _Nonnull task) {
  877. AWSCognitoIdentityProviderRespondToAuthChallengeResponse *response = task.result;
  878. AWSCognitoIdentityProviderAuthenticationResultType * authResult = response.authenticationResult;
  879. AWSCognitoIdentityUserSession * session = [[AWSCognitoIdentityUserSession alloc] initWithIdToken:authResult.idToken accessToken:authResult.accessToken refreshToken:authResult.refreshToken expiresIn:authResult.expiresIn];
  880. //last step is to perform device auth if device key is supplied or we are being challenged with device auth
  881. if(authResult.latestDeviceMetadata != nil || response.challengeName == AWSCognitoIdentityProviderChallengeNameTypeDeviceSrpAuth){
  882. return [self performDeviceAuth: task session:session];
  883. }else{
  884. [self updateUsernameAndPersistTokens:session];
  885. return [AWSTask taskWithResult:session];
  886. }
  887. }] continueWithBlock:^id _Nullable(AWSTask * _Nonnull task) {
  888. [authenticationDelegate didCompleteMultifactorAuthenticationStepWithError:task.error];
  889. if([task isCancelled]){
  890. return task;
  891. }
  892. if(task.error){
  893. //retry on error
  894. return [self mfaAuthInternal:deliveryMedium destination:destination authState:authState challengeName: challengeName authenticationDelegate:authenticationDelegate];
  895. }else {
  896. return task;
  897. }
  898. }];
  899. }];
  900. }
  901. /**
  902. * Update this user's attributes
  903. */
  904. -(AWSTask<AWSCognitoIdentityUserUpdateAttributesResponse *>*) updateAttributes: (NSArray<AWSCognitoIdentityUserAttributeType *>*) attributes {
  905. AWSCognitoIdentityProviderUpdateUserAttributesRequest *request = [AWSCognitoIdentityProviderUpdateUserAttributesRequest new];
  906. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  907. request.accessToken = task.result.accessToken.tokenString;
  908. NSMutableArray *userAttributes = [NSMutableArray new];
  909. request.userAttributes = userAttributes;
  910. for (AWSCognitoIdentityUserAttributeType * attribute in attributes) {
  911. AWSCognitoIdentityProviderAttributeType *apiAttribute = [AWSCognitoIdentityProviderAttributeType new];
  912. apiAttribute.name = attribute.name;
  913. apiAttribute.value = attribute.value;
  914. [userAttributes addObject: apiAttribute];
  915. }
  916. return [[self.pool.client updateUserAttributes:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderUpdateUserAttributesResponse *> * _Nonnull task) {
  917. AWSCognitoIdentityUserUpdateAttributesResponse * response = [AWSCognitoIdentityUserUpdateAttributesResponse new];
  918. [response aws_copyPropertiesFromObject:task.result];
  919. // drop id and access token to force a refresh
  920. [self clearSession];
  921. return [AWSTask taskWithResult:response];
  922. }];
  923. }];
  924. }
  925. /**
  926. * Delete the attributes specified by attributeNames
  927. */
  928. -(AWSTask<AWSCognitoIdentityUserDeleteAttributesResponse *>*) deleteAttributes: (NSArray<NSString *>*) attributeNames {
  929. AWSCognitoIdentityProviderDeleteUserAttributesRequest *request = [AWSCognitoIdentityProviderDeleteUserAttributesRequest new];
  930. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  931. request.accessToken = task.result.accessToken.tokenString;
  932. request.userAttributeNames = attributeNames;
  933. return [[self.pool.client deleteUserAttributes:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderDeleteUserAttributesResponse *> * _Nonnull task) {
  934. AWSCognitoIdentityUserDeleteAttributesResponse * response = [AWSCognitoIdentityUserDeleteAttributesResponse new];
  935. return [AWSTask taskWithResult:response];
  936. }];
  937. }];
  938. }
  939. /**
  940. * Verify a user attribute upon receiving the verification code.
  941. */
  942. -(AWSTask<AWSCognitoIdentityUserVerifyAttributeResponse *>*) verifyAttribute: (NSString *) attributeName code: (NSString *) code {
  943. AWSCognitoIdentityProviderVerifyUserAttributeRequest *request = [AWSCognitoIdentityProviderVerifyUserAttributeRequest new];
  944. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  945. request.accessToken = task.result.accessToken.tokenString;
  946. request.attributeName = attributeName;
  947. request.code = code;
  948. return [[self.pool.client verifyUserAttribute:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderVerifyUserAttributeResponse *> * _Nonnull task) {
  949. AWSCognitoIdentityUserVerifyAttributeResponse * response = [AWSCognitoIdentityUserVerifyAttributeResponse new];
  950. return [AWSTask taskWithResult:response];
  951. }];
  952. }];
  953. }
  954. /**
  955. * Request a verification code to verify an attribute.
  956. */
  957. -(AWSTask<AWSCognitoIdentityUserGetAttributeVerificationCodeResponse *>*) getAttributeVerificationCode: (NSString *) attributeName {
  958. AWSCognitoIdentityProviderGetUserAttributeVerificationCodeRequest *request = [AWSCognitoIdentityProviderGetUserAttributeVerificationCodeRequest new];
  959. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  960. request.accessToken = task.result.accessToken.tokenString;
  961. request.attributeName = attributeName;
  962. return [[self.pool.client getUserAttributeVerificationCode:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderGetUserAttributeVerificationCodeResponse *> * _Nonnull task) {
  963. AWSCognitoIdentityUserGetAttributeVerificationCodeResponse * response = [AWSCognitoIdentityUserGetAttributeVerificationCodeResponse new];
  964. [response aws_copyPropertiesFromObject:task.result];
  965. return [AWSTask taskWithResult:response];
  966. }];
  967. }];
  968. }
  969. /**
  970. * Set the user settings for this user such as MFA
  971. */
  972. -(AWSTask<AWSCognitoIdentityUserSetUserSettingsResponse *>*) setUserSettings: (AWSCognitoIdentityUserSettings *) settings; {
  973. AWSCognitoIdentityProviderSetUserSettingsRequest *request = [AWSCognitoIdentityProviderSetUserSettingsRequest new];
  974. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  975. request.accessToken = task.result.accessToken.tokenString;
  976. NSMutableArray * mfaOptions = [NSMutableArray new];
  977. for(AWSCognitoIdentityUserMFAOption* mfaOption in settings.mfaOptions){
  978. [mfaOptions addObject:[mfaOption mfaOptionTypeValue]];
  979. }
  980. request.MFAOptions = mfaOptions;
  981. return [[self.pool.client setUserSettings:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderSetUserSettingsResponse *> * _Nonnull task) {
  982. AWSCognitoIdentityUserSetUserSettingsResponse * response = [AWSCognitoIdentityUserSetUserSettingsResponse new];
  983. [response aws_copyPropertiesFromObject:task.result];
  984. return [AWSTask taskWithResult:response];
  985. }];
  986. }];
  987. }
  988. /**
  989. * Set the user settings for this user such as MFA
  990. */
  991. -(AWSTask<AWSCognitoIdentityUserSetUserSettingsResponse *>*) setUserMfaPreference: (AWSCognitoIdentityUserMfaPreferences*) settings; {
  992. AWSCognitoIdentityProviderSetUserMFAPreferenceRequest *request = [AWSCognitoIdentityProviderSetUserMFAPreferenceRequest new];
  993. if(settings.smsMfa != nil){
  994. AWSCognitoIdentityProviderSMSMfaSettingsType* smsMfaSettings = [AWSCognitoIdentityProviderSMSMfaSettingsType new];
  995. smsMfaSettings.enabled = [NSNumber numberWithBool:settings.smsMfa.enabled];
  996. smsMfaSettings.preferredMfa = [NSNumber numberWithBool:settings.smsMfa.preferred];
  997. request.SMSMfaSettings = smsMfaSettings;
  998. }
  999. if(settings.softwareTokenMfa != nil){
  1000. AWSCognitoIdentityProviderSoftwareTokenMfaSettingsType* softwareTokenMfaSettings = [AWSCognitoIdentityProviderSoftwareTokenMfaSettingsType new];
  1001. softwareTokenMfaSettings.enabled = [NSNumber numberWithBool:settings.softwareTokenMfa.enabled];
  1002. softwareTokenMfaSettings.preferredMfa = [NSNumber numberWithBool:settings.softwareTokenMfa.preferred];
  1003. request.softwareTokenMfaSettings = softwareTokenMfaSettings;
  1004. }
  1005. return [[[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1006. request.accessToken = task.result.accessToken.tokenString;
  1007. return [self.pool.client setUserMFAPreference:request];
  1008. }] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderSetUserMFAPreferenceResponse *> * _Nonnull task) {
  1009. AWSCognitoIdentityUserSetUserMfaPreferenceResponse * response = [AWSCognitoIdentityUserSetUserMfaPreferenceResponse new];
  1010. [response aws_copyPropertiesFromObject:task.result];
  1011. return [AWSTask taskWithResult:response];
  1012. }];
  1013. }
  1014. /**
  1015. * Start the process of associating a software token
  1016. */
  1017. -(AWSTask<AWSCognitoIdentityUserAssociateSoftwareTokenResponse *>*) associateSoftwareToken {
  1018. AWSCognitoIdentityProviderAssociateSoftwareTokenRequest * request = [AWSCognitoIdentityProviderAssociateSoftwareTokenRequest new];
  1019. return [[[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1020. request.accessToken = task.result.accessToken.tokenString;
  1021. return [self.pool.client associateSoftwareToken:request];
  1022. }] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserAssociateSoftwareTokenResponse *> * _Nonnull task) {
  1023. AWSCognitoIdentityUserAssociateSoftwareTokenResponse * response = [AWSCognitoIdentityUserAssociateSoftwareTokenResponse new];
  1024. [response aws_copyPropertiesFromObject:task.result];
  1025. return [AWSTask taskWithResult:response];
  1026. }];
  1027. }
  1028. /**
  1029. * Complete the process of associating a software token by verifying the code and setting device friendly name
  1030. */
  1031. -(AWSTask<AWSCognitoIdentityUserVerifySoftwareTokenResponse *>*) verifySoftwareToken: (NSString*) userCode friendlyDeviceName: (NSString* _Nullable) friendlyDeviceName {
  1032. AWSCognitoIdentityProviderVerifySoftwareTokenRequest *request = [AWSCognitoIdentityProviderVerifySoftwareTokenRequest new];
  1033. request.friendlyDeviceName = friendlyDeviceName;
  1034. request.userCode = userCode;
  1035. return [[[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1036. request.accessToken = task.result.accessToken.tokenString;
  1037. return [self.pool.client verifySoftwareToken:request];
  1038. }] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderVerifySoftwareTokenResponse *> * _Nonnull task) {
  1039. AWSCognitoIdentityUserVerifySoftwareTokenResponse * response = [AWSCognitoIdentityUserVerifySoftwareTokenResponse new];
  1040. [response aws_copyPropertiesFromObject:task.result];
  1041. return [AWSTask taskWithResult:response];
  1042. }];
  1043. }
  1044. /**
  1045. * Delete this user
  1046. */
  1047. -(AWSTask*) deleteUser {
  1048. AWSCognitoIdentityProviderDeleteUserRequest *request = [AWSCognitoIdentityProviderDeleteUserRequest new];
  1049. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1050. request.accessToken = task.result.accessToken.tokenString;
  1051. return [self.pool.client deleteUser:request];
  1052. }];
  1053. }
  1054. -(void) signOut {
  1055. if(self.username){
  1056. NSArray *keys = self.pool.keychain.allKeys;
  1057. NSString *keyChainPrefix = [[self keyChainNamespaceClientId] stringByAppendingString:@"."];
  1058. for (NSString *key in keys) {
  1059. //clear tokens associated with this user
  1060. if([key hasPrefix:keyChainPrefix]){
  1061. [self.pool.keychain removeItemForKey:key];
  1062. }
  1063. }
  1064. }
  1065. }
  1066. -(void) signOutAndClearLastKnownUser{
  1067. [self signOut];
  1068. [self.pool clearLastKnownUser];
  1069. }
  1070. -(void) clearSession{
  1071. if(self.username){
  1072. NSString * keyChainNamespace = [self keyChainNamespaceClientId];
  1073. NSString * idTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserIdToken];
  1074. NSString * accessTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserAccessToken];
  1075. [self.pool.keychain removeItemForKey:idTokenKey];
  1076. [self.pool.keychain removeItemForKey:accessTokenKey];
  1077. }
  1078. }
  1079. - (NSString *) refreshTokenFromKeyChain: (NSString *) keyChainNamespace {
  1080. NSString * refreshTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserRefreshToken];
  1081. NSString * refreshToken = self.pool.keychain[refreshTokenKey];
  1082. return refreshToken;
  1083. }
  1084. -(BOOL) isSignedIn {
  1085. if(self.username == nil){
  1086. return NO;
  1087. }
  1088. NSString * keyChainNamespace = [self keyChainNamespaceClientId];
  1089. NSString * refreshToken = [self refreshTokenFromKeyChain:keyChainNamespace];
  1090. return refreshToken != nil;
  1091. }
  1092. - (AWSTask<AWSCognitoIdentityUserGlobalSignOutResponse *> *) globalSignOut {
  1093. AWSCognitoIdentityProviderGlobalSignOutRequest *request = [AWSCognitoIdentityProviderGlobalSignOutRequest new];
  1094. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1095. request.accessToken = task.result.accessToken.tokenString;
  1096. return [[self.pool.client globalSignOut:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderGlobalSignOutResponse *> * _Nonnull task) {
  1097. [self signOut];
  1098. return task;
  1099. }];
  1100. }];
  1101. }
  1102. - (AWSTask<AWSCognitoIdentityUserListDevicesResponse *> *) listDevices:(int) limit paginationToken:(NSString * _Nullable) paginationToken {
  1103. AWSCognitoIdentityProviderListDevicesRequest *request = [AWSCognitoIdentityProviderListDevicesRequest new];
  1104. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1105. request.accessToken = task.result.accessToken.tokenString;
  1106. request.paginationToken = paginationToken;
  1107. request.limit = [NSNumber numberWithInt:limit];
  1108. return [[self.pool.client listDevices:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderListDevicesResponse *> * _Nonnull task) {
  1109. AWSCognitoIdentityUserListDevicesResponse * response = [AWSCognitoIdentityUserListDevicesResponse new];
  1110. [response aws_copyPropertiesFromObject:task.result];
  1111. return [AWSTask taskWithResult:response];
  1112. }];
  1113. }];
  1114. }
  1115. - (AWSTask<AWSCognitoIdentityUserUpdateDeviceStatusResponse *> *) updateDeviceStatus: (NSString *) deviceId remembered:(BOOL) remembered {
  1116. AWSCognitoIdentityProviderUpdateDeviceStatusRequest *request = [AWSCognitoIdentityProviderUpdateDeviceStatusRequest new];
  1117. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1118. request.accessToken = task.result.accessToken.tokenString;
  1119. request.deviceKey = deviceId;
  1120. request.deviceRememberedStatus = remembered ? AWSCognitoIdentityProviderDeviceRememberedStatusTypeRemembered : AWSCognitoIdentityProviderDeviceRememberedStatusTypeNotRemembered;
  1121. return [[self.pool.client updateDeviceStatus:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderUpdateDeviceStatusRequest *> * _Nonnull task) {
  1122. AWSCognitoIdentityUserUpdateDeviceStatusResponse * response = [AWSCognitoIdentityUserUpdateDeviceStatusResponse new];
  1123. [response aws_copyPropertiesFromObject:task.result];
  1124. return [AWSTask taskWithResult:response];
  1125. }];
  1126. }];
  1127. }
  1128. - (AWSTask<AWSCognitoIdentityUserUpdateDeviceStatusResponse *> *) updateDeviceStatus: (BOOL) remembered {
  1129. AWSCognitoIdentityUserDeviceCredentials * credentials = [self getDeviceCredentials];
  1130. if(credentials){
  1131. return [self updateDeviceStatus:[self getDeviceCredentials].deviceId remembered:remembered];
  1132. }else{
  1133. return [self deviceNotTrackedError];
  1134. }
  1135. }
  1136. - (AWSTask<AWSCognitoIdentityUserGetDeviceResponse *> *) getDevice: (NSString *) deviceId {
  1137. AWSCognitoIdentityProviderGetDeviceRequest *request = [AWSCognitoIdentityProviderGetDeviceRequest new];
  1138. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1139. request.accessToken = task.result.accessToken.tokenString;
  1140. request.deviceKey = deviceId;
  1141. return [[self.pool.client getDevice:request] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityProviderGetDeviceResponse *> * _Nonnull task) {
  1142. AWSCognitoIdentityUserGetDeviceResponse * response = [AWSCognitoIdentityUserGetDeviceResponse new];
  1143. [response aws_copyPropertiesFromObject:task.result];
  1144. return [AWSTask taskWithResult:response];
  1145. }];
  1146. }];
  1147. }
  1148. - (AWSTask<AWSCognitoIdentityUserGetDeviceResponse *> *) getDevice {
  1149. AWSCognitoIdentityUserDeviceCredentials * credentials = [self getDeviceCredentials];
  1150. if(credentials){
  1151. return [self getDevice:credentials.deviceId];
  1152. }else{
  1153. return [self deviceNotTrackedError];
  1154. }
  1155. }
  1156. - (AWSTask *) forgetDevice {
  1157. AWSCognitoIdentityUserDeviceCredentials * credentials = [self getDeviceCredentials];
  1158. if(credentials){
  1159. return [self forgetDevice:credentials.deviceId];
  1160. }else{
  1161. return [self deviceNotTrackedError];
  1162. }
  1163. }
  1164. - (AWSTask *) deviceNotTrackedError {
  1165. return [AWSTask taskWithError:[NSError errorWithDomain:AWSCognitoIdentityProviderErrorDomain code:AWSCognitoIdentityProviderClientErrorDeviceNotTracked userInfo:@{NSLocalizedDescriptionKey: @"This device does not have an id, either it was never tracked or previously forgotten."}]];
  1166. }
  1167. - (AWSTask *) forgetDevice: (NSString *) deviceId {
  1168. AWSCognitoIdentityProviderForgetDeviceRequest * request = [AWSCognitoIdentityProviderForgetDeviceRequest new];
  1169. return [[self getSession] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
  1170. request.accessToken = task.result.accessToken.tokenString;
  1171. request.deviceKey = deviceId;
  1172. return [[self.pool.client forgetDevice:request] continueWithSuccessBlock:^id _Nullable(AWSTask * _Nonnull task) {
  1173. AWSCognitoIdentityUserDeviceCredentials * credentials = [self getDeviceCredentials];
  1174. //if it was this device that was forgotten and call was successful, clear cached device.
  1175. if(credentials && credentials.deviceId && [credentials.deviceId isEqualToString:deviceId]){
  1176. [self forgetDeviceInternal];
  1177. }
  1178. return task;
  1179. }];
  1180. }];
  1181. }
  1182. - (void) updateUsernameAndPersistTokens: (AWSCognitoIdentityUserSession *) session {
  1183. [self.pool setCurrentUser:self.username];
  1184. NSString * keyChainNamespace = [self keyChainNamespaceClientId];
  1185. if(session.idToken){
  1186. NSString * idTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserIdToken];
  1187. self.pool.keychain[idTokenKey] = session.idToken.tokenString;
  1188. }
  1189. if(session.accessToken){
  1190. NSString * accessTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserAccessToken];
  1191. self.pool.keychain[accessTokenKey] = session.accessToken.tokenString;
  1192. }
  1193. if(session.refreshToken){
  1194. NSString * refreshTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserRefreshToken];
  1195. self.pool.keychain[refreshTokenKey] = session.refreshToken.tokenString;
  1196. }
  1197. if(session.expirationTime){
  1198. NSString * expirationTokenKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserTokenExpiration];
  1199. self.pool.keychain[expirationTokenKey] = [session.expirationTime aws_stringValue:AWSDateISO8601DateFormat1];
  1200. }
  1201. }
  1202. - (void) persistDevice:(NSString *) deviceKey deviceSecret: (NSString *) deviceSecret deviceGroup: (NSString *) deviceGroup {
  1203. NSString * keyChainNamespace = [self keyChainNamespacePoolId];
  1204. if(deviceKey){
  1205. NSString * deviceIdKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceId];
  1206. self.pool.keychain[deviceIdKey] = deviceKey;
  1207. }
  1208. if(deviceSecret){
  1209. NSString * deviceSecretKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceSecret];
  1210. self.pool.keychain[deviceSecretKey] = deviceSecret;
  1211. }
  1212. if(deviceGroup){
  1213. NSString * deviceGroupKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceGroup];
  1214. self.pool.keychain[deviceGroupKey] = deviceGroup;
  1215. }
  1216. }
  1217. - (void) forgetDeviceInternal {
  1218. NSString * keyChainNamespace = [self keyChainNamespacePoolId];
  1219. NSString * deviceIdKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceId];
  1220. self.pool.keychain[deviceIdKey] = nil;
  1221. NSString * deviceSecretKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceSecret];
  1222. self.pool.keychain[deviceSecretKey] = nil;
  1223. NSString * deviceGroupKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceGroup];
  1224. self.pool.keychain[deviceGroupKey] = nil;
  1225. }
  1226. - (AWSCognitoIdentityUserDeviceCredentials *) getDeviceCredentials {
  1227. AWSCognitoIdentityUserDeviceCredentials *deviceCredentials = [[AWSCognitoIdentityUserDeviceCredentials alloc] initWithUser:self];
  1228. if(deviceCredentials.deviceId && deviceCredentials.deviceSecret){
  1229. return deviceCredentials;
  1230. }else {
  1231. return nil;
  1232. }
  1233. }
  1234. - (NSString *) keyChainNamespaceClientId {
  1235. return [NSString stringWithFormat:@"%@.%@", self.pool.userPoolConfiguration.clientId, self.username];
  1236. }
  1237. - (NSString *) keyChainNamespacePoolId {
  1238. return [NSString stringWithFormat:@"%@.%@", self.pool.userPoolConfiguration.poolId, self.username];
  1239. }
  1240. - (NSString *) asfDeviceId {
  1241. NSString *asfDeviceId = [self getDeviceCredentials].deviceId;
  1242. if(asfDeviceId == nil){
  1243. NSString * keyChainNamespace = [self keyChainNamespacePoolId];
  1244. NSString * asfDeviceIdKey = [self keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserAsfDeviceId];
  1245. asfDeviceId = self.pool.keychain[asfDeviceIdKey];
  1246. if(asfDeviceId == nil){
  1247. asfDeviceId = [[[NSUUID UUID] UUIDString] lowercaseString];
  1248. self.pool.keychain[asfDeviceIdKey] = asfDeviceId;
  1249. }
  1250. }
  1251. return asfDeviceId;
  1252. }
  1253. /**
  1254. * Get a namespaced keychain key given a namespace and key
  1255. */
  1256. - (NSString *) keyChainKey: (NSString *) namespace key:(const NSString *) key {
  1257. return [NSString stringWithFormat:@"%@.%@", namespace, key];
  1258. }
  1259. - (NSString*) strippedPoolName {
  1260. return [self.pool.userPoolConfiguration.poolId substringFromIndex:[self.pool.userPoolConfiguration.poolId rangeOfString:@"_" ].location+1];
  1261. }
  1262. - (NSString*) deviceId {
  1263. AWSCognitoIdentityUserDeviceCredentials *deviceCredentials = [self getDeviceCredentials];
  1264. return deviceCredentials?deviceCredentials.deviceId:nil;
  1265. }
  1266. @end
  1267. @implementation AWSCognitoIdentityUserSession
  1268. -(instancetype) initWithIdToken:(NSString *)idToken accessToken:(NSString *)accessToken refreshToken:(NSString *)refreshToken expiresIn:(NSNumber *) expiresIn {
  1269. self = [self initWithIdToken:idToken accessToken:accessToken refreshToken:refreshToken expirationTime:[NSDate dateWithTimeIntervalSinceNow:expiresIn.doubleValue]];
  1270. return self;
  1271. }
  1272. -(instancetype) initWithIdToken:(NSString *)idToken accessToken:(NSString *)accessToken refreshToken:(NSString *)refreshToken expirationTime:(NSDate *) expirationTime {
  1273. self = [super init];
  1274. if(self != nil) {
  1275. self.idToken = [[AWSCognitoIdentityUserSessionToken alloc] initWithToken:idToken];
  1276. self.accessToken = [[AWSCognitoIdentityUserSessionToken alloc] initWithToken:accessToken];
  1277. self.refreshToken = [[AWSCognitoIdentityUserSessionToken alloc] initWithToken:refreshToken];
  1278. self.expirationTime = expirationTime;
  1279. }
  1280. return self;
  1281. }
  1282. @end
  1283. @implementation AWSCognitoIdentityUserSessionToken
  1284. -(instancetype) initWithToken:(NSString *)token {
  1285. if(token == nil){
  1286. return nil;
  1287. }
  1288. self = [super init];
  1289. if(self != nil) {
  1290. self.tokenString = token;
  1291. }
  1292. return self;
  1293. }
  1294. -(NSDictionary<NSString *, NSString*> *) claims {
  1295. return [self tokenClaims];
  1296. }
  1297. -(NSDictionary<NSString *, id> *) tokenClaims {
  1298. NSDictionary * result = nil;
  1299. NSArray *pieces = [self.tokenString componentsSeparatedByString:@"."];
  1300. if(pieces.count > 2){
  1301. NSString * claims = pieces[1];
  1302. //JWT is not padded with =, pad it if necessary
  1303. NSUInteger paddedLength = claims.length + (4 - (claims.length % 4)) % 4;;
  1304. claims = [claims stringByPaddingToLength:paddedLength withString:@"=" startingAtIndex:0];
  1305. NSData * claimsData = [[NSData alloc] initWithBase64EncodedString:claims options:NSDataBase64DecodingIgnoreUnknownCharacters];
  1306. NSError *error = nil;
  1307. if(claimsData != nil){
  1308. result = [NSJSONSerialization JSONObjectWithData:claimsData options:kNilOptions error:&error];
  1309. if(error) {
  1310. AWSDDLogError(@"Unable to deserialize token claims: %@", error);
  1311. }
  1312. } else {
  1313. AWSDDLogError(@"Token is not valid base64");
  1314. }
  1315. }
  1316. return result;
  1317. }
  1318. @end
  1319. @implementation AWSCognitoIdentityUserSettings
  1320. @end
  1321. @implementation AWSCognitoIdentityUserMfaPreferences
  1322. @end
  1323. @implementation AWSCognitoIdentityUserMfaType
  1324. - (instancetype) initWithEnabled:(BOOL) enabled preferred:(BOOL) preferred {
  1325. if(self = [super init]){
  1326. self.enabled = enabled;
  1327. self.preferred = preferred;
  1328. }
  1329. return self;
  1330. }
  1331. @end
  1332. @implementation AWSCognitoIdentityUserMFAOption
  1333. - (AWSCognitoIdentityProviderMFAOptionType *) mfaOptionTypeValue{
  1334. AWSCognitoIdentityProviderMFAOptionType *result = [AWSCognitoIdentityProviderMFAOptionType new];
  1335. [result aws_copyPropertiesFromObject:self];
  1336. return result;
  1337. }
  1338. @end
  1339. @implementation AWSCognitoIdentityUserAttributeType
  1340. - (instancetype) initWithName: (NSString *) name value: (NSString *) value{
  1341. if(self = [super init]){
  1342. self.name = name;
  1343. self.value = value;
  1344. }
  1345. return self;
  1346. }
  1347. @end
  1348. @implementation AWSCognitoIdentityUserConfirmSignUpResponse
  1349. @end
  1350. @implementation AWSCognitoIdentityUserResendConfirmationCodeResponse
  1351. @end
  1352. @implementation AWSCognitoIdentityUserGetDetailsResponse
  1353. @end
  1354. @implementation AWSCognitoIdentityUserForgotPasswordResponse
  1355. @end
  1356. @implementation AWSCognitoIdentityUserConfirmForgotPasswordResponse
  1357. @end
  1358. @implementation AWSCognitoIdentityUserChangePasswordResponse
  1359. @end
  1360. @implementation AWSCognitoIdentityUserUpdateAttributesResponse
  1361. @end
  1362. @implementation AWSCognitoIdentityUserDeleteAttributesResponse
  1363. @end
  1364. @implementation AWSCognitoIdentityUserVerifyAttributeResponse
  1365. @end
  1366. @implementation AWSCognitoIdentityUserGetAttributeVerificationCodeResponse
  1367. @end
  1368. @implementation AWSCognitoIdentityUserSetUserSettingsResponse
  1369. @end
  1370. @implementation AWSCognitoIdentityUserListDevicesResponse
  1371. @end
  1372. @implementation AWSCognitoIdentityUserUpdateDeviceStatusResponse
  1373. @end
  1374. @implementation AWSCognitoIdentityUserGlobalSignOutResponse
  1375. @end
  1376. @implementation AWSCognitoIdentityUserGetDeviceResponse
  1377. @end
  1378. @implementation AWSCognitoIdentityUserAssociateSoftwareTokenResponse
  1379. @end
  1380. @implementation AWSCognitoIdentityUserVerifySoftwareTokenResponse
  1381. @end
  1382. @implementation AWSCognitoIdentityUserSetUserMfaPreferenceResponse
  1383. @end
  1384. @implementation AWSCognitoIdentityUserDeviceCredentials
  1385. - (instancetype) initWithUser:(AWSCognitoIdentityUser *) user {
  1386. if(self = [super init]){
  1387. _user = user;
  1388. }
  1389. return self;
  1390. }
  1391. - (NSString *) deviceId {
  1392. NSString * keyChainNamespace = [self.user keyChainNamespacePoolId];
  1393. NSString * deviceIdKey = [self.user keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceId];
  1394. return self.user.pool.keychain[deviceIdKey];
  1395. }
  1396. - (NSString *) deviceSecret {
  1397. NSString * keyChainNamespace = [self.user keyChainNamespacePoolId];
  1398. NSString * deviceSecretKey = [self.user keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceSecret];
  1399. return self.user.pool.keychain[deviceSecretKey];
  1400. }
  1401. - (NSString *) deviceGroup {
  1402. NSString * keyChainNamespace = [self.user keyChainNamespacePoolId];
  1403. NSString * deviceGroupKey = [self.user keyChainKey:keyChainNamespace key:AWSCognitoIdentityUserDeviceGroup];
  1404. return self.user.pool.keychain[deviceGroupKey];
  1405. }
  1406. @end