title: Confused with logic of login flow in AWS Developer Authenticated Identities
body: <p>I believe that I have an incorrect flow for implementing Developer Authenticated Identities and I keep hearing and doing different things on the web. So I thought I'd present my entire flow and hear what the correct way of doing this was and present some questions and errors at the bottom. </p>
  
  <p>Initially, I have a user login with a password and username (I'm using nsuserdefaults only temporarily, I will use KeyChain later). </p>
  
  <p>Note: I also have a callback that goes all the way down to see if I properly authenticated a user. </p>
  
  <p>Login Method: </p>
  
  <pre><code>-(void)loginBusyTimeUser:(void(^)(BOOL))callBack{
  //initialize nsuserdefualts should be keychain later
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  
  NSMutableDictionary *post = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                        [defaults objectForKey:@"username"], @"username",
                        [defaults objectForKey:@"password"], @"password",
                        nil];
  NSError *error;
  NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:0 error:&amp;error];
  NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postData length]];
  NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  [request setURL:[NSURL URLWithString:@"SOMELOGINURL"]];
  [request setHTTPMethod:@"POST"];
  [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
  [request setHTTPBody:postData];
  NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
      NSDictionary *newJSON = [NSJSONSerialization JSONObjectWithData:data
                                                              options:0
                                                                error:&amp;error];
      if(!newJSON || [newJSON objectForKey:@"errorMessage"]){
          NSLog(@"%@",newJSON);
          callBack(false);
          NSLog(@"DID NOT AUTHENTICATE");
      }else{
          NSLog(@"%@",newJSON);
          [defaults setValue:[newJSON objectForKey:@"Token"] forKey:@"Token"];
          [defaults setValue:[newJSON objectForKey:@"IdentityId"] forKey:@"IdentityId"];
          [self authenticateUser:^(BOOL call){
              callBack(call);
          }];
      }
  }] resume];
  </code></pre>
  
  <p>}</p>
  
  <p>If Everything is successful, I then authenticate my user with AWS Cognito with the developer authenticated flow: </p>
  
  <pre><code>-(void)authenticateUser:(void(^)(BOOL))callBack{
  //Now after making sure that your user's credentials are sound, then initialize the IdentityProvider, in this case
  //BusytimeAuthenticated
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  id&lt;AWSCognitoIdentityProvider&gt; identityProvider = [[BusytimeAuthenticated alloc] initWithRegionType:AWSRegionUSEast1
                                                                                           identityId:nil
                                                                                       identityPoolId:@"SOMEPOOLID"
                                                                              logins:@{@"cognito-identity.amazonaws.com": [defaults objectForKey:@"Token"]}
                                                                                         providerName:@"cognito-identity.amazonaws.com"
                                                     ];
  
  credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
                                                                 identityProvider:identityProvider
                                                                    unauthRoleArn:nil
                                                                      authRoleArn:nil];
  configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1
                                              credentialsProvider:self.credentialsProvider];
  AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
  [[credentialsProvider refresh] continueWithBlock:^id(AWSTask *task){
      if([task isFaulted]){
          callBack(false);
      }else{
      callBack(true);
      }
      return [defaults objectForKey:@"Token"];
  }];
  </code></pre>
  
  <p>}</p>
  
  <p>Then the refresh method causes some errors so I'll show my "BusytimeAuthenticated" class (.m) </p>
  
  <pre><code>//
  //  BusytimeAuthenticated.m
  //  BusyTime
  //
  //  Created by akash kakumani on 10/14/15.
  //  Copyright (c) 2015 BusyTime. All rights reserved.
  //
  
  #import "BusytimeAuthenticated.h"
  
  @interface BusytimeAuthenticated()
  @property (strong, atomic) NSString *providerName;
  @property (strong, atomic) NSString *token;
  @end
  
  @implementation BusytimeAuthenticated
  @synthesize providerName=_providerName;
  @synthesize token=_token;
  
  
  - (instancetype)initWithRegionType:(AWSRegionType)regionType
                          identityId:(NSString *)identityId
                      identityPoolId:(NSString *)identityPoolId
                              logins:(NSDictionary *)logins
                        providerName:(NSString *)providerName{
      if (self = [super initWithRegionType:regionType identityId:identityId accountId:nil identityPoolId:identityPoolId logins:logins]) {
          self.providerName = providerName;
      }
      return self;
  }
  
  // Return the developer provider name which you choose while setting up the
  // identity pool in the Amazon Cognito Console
  
  - (BOOL)authenticatedWithProvider {
      return [self.logins objectForKey:self.providerName] != nil;
  }
  
  
  // If the app has a valid identityId return it, otherwise get a valid
  // identityId from your backend.
  
  - (AWSTask *)getIdentityId {
      // already cached the identity id, return it
      if (self.identityId) {
          return [AWSTask taskWithResult:nil];
      }
      // not authenticated with our developer provider
      else if (![self authenticatedWithProvider]) {
          return [super getIdentityId];
      }
      // authenticated with our developer provider, use refresh logic to get id/token pair
      else {
          return [[AWSTask taskWithResult:nil] continueWithBlock:^id(AWSTask *task) {
              if (!self.identityId) {
                  return [self refresh];
              }
              return [AWSTask taskWithResult:self.identityId];
          }];
      }
  
  }
  
  
  // Use the refresh method to communicate with your backend to get an
  // identityId and token.
  
  - (AWSTask *)refresh {
      NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
      if (![self authenticatedWithProvider]) {
          return [super getIdentityId];
      }else{
          NSDictionary *post = [[NSDictionary alloc] initWithObjectsAndKeys:
                                [defaults objectForKey:@"username"], @"username",
                                [defaults objectForKey:@"password"], @"password",
                                nil];
          NSError *error;
          NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:0 error:&amp;error];
          NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postData length]];
          NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
          [request setURL:[NSURL URLWithString:@"SOMELOGINURL"]];
          [request setHTTPMethod:@"POST"];
          [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
          [request setHTTPBody:postData];
          NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
          [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
              NSDictionary *newJSON = [NSJSONSerialization JSONObjectWithData:data
                                                                      options:0
                                                                        error:&amp;error];
              if(!newJSON){
              NSLog(@"Failure in refresh: %@",newJSON);
              }else{
              NSLog(@"The IdentityID in the refresh method: %@",[newJSON objectForKey:@"IdentityId" ]);
              NSLog(@"The token in the refresh method: %@",[newJSON objectForKey:@"Token" ]);
              self.identityId = [newJSON objectForKey:@"IdentityId" ];
              self.token = [newJSON objectForKey:@"Token" ];
              }
          }] resume];
  
          return [AWSTask taskWithResult:self.identityId];
      }
      return [AWSTask taskWithResult:self.identityId];
  }
  
  
  @end
  </code></pre>
  
  <p>Some Questions I had: </p>
  
  <ol>
  <li><p>Is the DeveloperAuthenticationClient necessary to solve my problems? I saw the sample app using them but I found them too confusing. </p></li>
  <li><p>Should I be using my ProviderName or should I be using "cognito-identity.amazonaws.com" </p></li>
  <li><p>I sometimes face a timeout error and found out that it could be the fact that my implementation of login (using API Gateway and a lambda method) could have some cold-start issues. The way I solved this is by increasing the timeout time to 20 seconds. Is this the correct way to solve this? </p></li>
  <li><p>I saw that in the sample app they use GetToken and Login as two separate things. I thought it would be easier if my login could also serve as my GetToken. Is this appropriate? </p></li>
  <li><p>Finally, please address any problems that you see with my code if time permits. </p></li>
  </ol>
  
  <p>Error: </p>
  
  <blockquote>
    <p>[Error] AWSCredentialsProvider.m line:527 |
    __40-[AWSCognitoCredentialsProvider refresh]_block_invoke352 | Unable to refresh. Error is [Error
    Domain=com.amazonaws.AWSCognitoCredentialsProviderErrorDomain Code=1
    "identityId shouldn't be nil"
    UserInfo={NSLocalizedDescription=identityId shouldn't be nil}]</p>
  </blockquote>
  
  <p>(I also found out the error above is related to the fact that self.identityId didn't get set because the request was in a block and other parts executed first and the solution is: </p>
  
  <pre><code>- (AWSTask *)refresh {
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  if (![self authenticatedWithProvider]) {
      return [super getIdentityId];
  }else{
      NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
      NSString *string = [defaults objectForKey:@"IdentityId"];
      self.identityId = string;
      return [AWSTask taskWithResult:[defaults objectForKey:@"IdentityId"]];
  }
  NSString *string = [defaults objectForKey:@"IdentityId"];
  return [AWSTask taskWithResult:[defaults objectForKey:@"IdentityId"]];
  }
  </code></pre>
  
  <p>But I believe that this isn't the correct implementation. )</p>
  
  <p>I believe my code was working at a certain point but stopped working after I upgraded to the new SDK. However, it might just be the fact that I hadn't noticed the error initially. </p>