1

The app is not getting successful API call after hours of inactivity.

Steps to reproduce the behavior:

  1. Creation of Amazon Cognito ID.
  2. API calls.
  3. Put the app in the background.
  4. Refresh code after 8 hours. (initializeAmazonCongnitoProviderWithCompletionBlock mentioned below is called first when the app comes to foreground only after 8 hours) Missing auth token will appear as suggested in the screenshot. Which AWS service(s) are affected?

enter image description here

- (void)initializeAmazonCognitoProviderWithCompletionBlock:(void (^)(void))completion { 
    [[UIApplication sharedApplication].keyWindow setUserInteractionEnabled:FALSE];
    NSString* AWSCognitoPoolID = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"AWSCognitoID"];
    AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc]  initWithRegionType:AMAZON_COGNITO_REGION identityPoolId:AWSCognitoPoolID];
    [credentialsProvider clearCredentials];
    AWSServiceConfiguration *configuration =[[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1  credentialsProvider:credentialsProvider];
    AWSServiceManager.defaultServiceManager.defaultServiceConfiguration =  configuration;
    
    [self getCognitoID:credentialsProvider CompletionBlock:^{
        [self expirationHandler:credentialsProvider CompletionBlock:^{
            dispatch_async(dispatch_get_main_queue(), ^{
              [[UIApplication sharedApplication].keyWindow setUserInteractionEnabled:TRUE];
            });
            completion();
        }];
    }]; 
}


- (void)expirationHandler:(AWSCognitoCredentialsProvider *)creds CompletionBlock: (void (^)(void))completion {
    [[creds credentials] continueWithBlock:^id(AWSTask *task) {
        if (task.error) {
            [self initializeAmazonCognitoProviderWithCompletionBlock:^{}];
        } else {
            AWSCredentials *cred = (AWSCredentials*) task.result;
            NSDateFormatter *dateFormat = [[NSDateFormatter alloc]init];
            [dateFormat setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];
            /* https://aws.amazon.com/premiumsupport/knowledge-center/security-token-expired/
             https://forums.aws.amazon.com/thread.jspa?threadID=166398
             We should fire timer before 5 minutes of expiration.
             NSString *expF = [dateFormat stringFromDate:cred.expiration];
             */
            [NSTimer scheduledTimerWithTimeInterval:cred.expiration.timeIntervalSinceNow -  300 target:self selector:@selector(initializeAmazonCognitoProviderWithCompletionBlock:) userInfo:nil repeats:NO];
            completion();
        }
        return nil;
    }];
}


- (void)getCognitoID:(AWSCognitoCredentialsProvider *)creds CompletionBlock: (void (^)(void))completion {
    [[creds getIdentityId] continueWithBlock:^id(AWSTask *task) {
        if (task.error) {
            NSLog(@"Error: %@", task.error);
            [self initializeAmazonCognitoProviderWithCompletionBlock:^{}];
        } else {
            NSString *cognitoId = task.result;
            NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
            if (![[standardDefaults valueForKey:@"UserCognitoID"] isEqualToString:cognitoId]) {
                [standardDefaults setObject:@"" forKey:BainPreferenceToken];
                [standardDefaults setInteger:0 forKey:@"registrationPostComplete"];
            }
            [standardDefaults setObject:cognitoId forKey:@"UserCognitoID"];
            [standardDefaults synchronize];
            completion();
        }
        return nil;
    }];
}
Community
  • 1
  • 1
DipakSonara
  • 2,598
  • 3
  • 29
  • 34
  • Can you show the code snippet where you make a call to the APIGateway API with the AWSCredentials? – Karthikeyan Oct 30 '18 at 20:40
  • @Karthikeyan We are using AWS task. And we are calling it in the completion of above method. – DipakSonara Oct 31 '18 at 05:45
  • Are you configuring UserPools as the Identity provider with your Identity Pool? If so, you need to obtain the token from UserPools client and federate it with the cognito credentials provider. This is the reason for MissingAuthenticationToken exception. – Karthikeyan Nov 05 '18 at 20:19
  • @Karthikeyan : Yes, I am using userPools as the Identity provider with Identity Pool. But I am not getting your point. How do I refresh the token/configuration of credential provider? Can you give me some code snippet? – DipakSonara Nov 06 '18 at 09:30
  • Sorry for the delayed response. I have the code snippets for federating the token into the credentials provider here: https://github.com/aws-amplify/aws-sdk-ios/issues/1218#issuecomment-466199118 – Karthikeyan Feb 21 '19 at 23:34

1 Answers1

0

AWSCognitoCredentialsProvider is an object that retrieves the credentials from Amazon Cognito and supplies credentials to sign the requests to AWS (for example to upload file to S3).

AWSCognitoIdentityProvider object in the SDK provides the abstraction for Amazon Cognito UserPools and helps you sign-in a user and retrieve the tokens that proves that the user is authenticated.

When a user is signed-in and AWSCognitoIdentityProvider has retrieved the tokens from Amazon Cognito UserPools, the tokens can be federated into the AWSCognitoCredentialsProvider in order to retrieve AWSCredentials and Cognito Identity Id for this authenticated user. To do so, you need to supply the token in the logins map and pass the logins map to AWSCognitoCredentialsProvider.

To accomplish this, you need to

1) create a class that confirms to the AWSIdentityProviderManager protocol and implements the logins method that returns a map of { providerLoginKey -> token }.

@interface AWSFederationManager : NSObject<AWSIdentityProviderManager>

@property (nonatomic, readonly, strong) AWSCognitoCredentialsProvider *credentialsProvider;

@end
@interface AWSFederationManager()

@property (nonatomic, readwrite, strong) AWSCognitoCredentialsProvider *credentialsProvider;

@end
@implementation AWSFederationManager

- (AWSTask<NSDictionary<NSString *, NSString *> *> *)logins {
        return [AWSTask taskWithResult:@{[<identityProviderName> : <token>}];
}

@end

2) Set the delegate of the AWSCognitoCredentialsProvider object to the class created in step (1).

@implementation AWSFederationManager

- (instancetype) initialize {
    [self.credentialsProvider setIdentityProviderManagerOnce:self];
}

@end

Now once the federation is successful, you will be able to retrieve the AWSCredentials and Cognito Identity Id.

You can refer Source for the reference implementation.

Karthikeyan
  • 1,411
  • 1
  • 13
  • 13