0

I am currently unable to authorize users using AWS iOS SDK V2 using Facebook and Google+ as the provider.

I'm not sure if its my setup on the AWS Developer Console, or whether its the code.

This is the role policy for the identity pools:

{  
"Version": "2012-10-17",  
"Statement": [{  
    "Action": [  
        "mobileanalytics:PutEvents",  
        "cognito-sync:*"  
    ],  
    "Effect": "Allow",  
    "Resource": ["*"]  
}]  

I do receive an unauthorized Cognito ID but when I try to use either Facebook or Google+ provider authentication, it does not work.

Unauthenticated User Confirmation

Once the Facebook login returns I can successfully use the user properties to extract the profile picture, name and email address. I then get the token (yes it is a very long string of characters) from the Facebook session and use it in the deviceID class:

- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
                        user:(id<FBGraphUser>)user {

//Populate viewcontoller with Facebook data
self.profilePictureView.profileID = user.id;
NSRange range = [user.name rangeOfString:@" "];
self.firstName.text = [user.name substringToIndex:range.location];
self.lastName.text = [user.name substringFromIndex:range.location+1];
self.emailAddress.text = [user objectForKey:@"email"];

//Get Facebook token, set then get Cognito device ID - in DeviceId class
NSString *token = FBSession.activeSession.accessTokenData.accessToken;
DeviceId *myDeviceId = [DeviceId sharedInstance];

cognitoDeviceId = [myDeviceId setFacebookToken:token];

}

The DeviceID class implementation is shown below:

#import "DeviceId.h"
#import <AWSiOSSDKv2/AWSCore.h>
#import <AWSCognitoSync/Cognito.h>

@implementation DeviceId

static NSString *cognitoId;
static DeviceId *_sharedInstance;
static AWSCognitoCredentialsProvider *credentialsProvider;
static AWSServiceConfiguration *configuration;

+ (DeviceId *) sharedInstance
{
    if (!_sharedInstance)
    {
        _sharedInstance = [[DeviceId alloc] init];
    }

    return _sharedInstance;
}

- (NSString *) getDeviceId
{
    return cognitoId;
}

- (void) setDeviceId
{
    /*
     * AWS Cognito
     */

        credentialsProvider = [AWSCognitoCredentialsProvider
                            credentialsWithRegionType:AWSRegionUSEast1
                            accountId:@"(accountID"
                            identityPoolId:@"(identityPool)"
                            unauthRoleArn:@"arn:aws:iam::(accountID):role/Cognito_(app)UsersUnauth_DefaultRole"
                            authRoleArn:@"arn:aws:iam::(accountID):role/Cognito_(app)UsersAuth_DefaultRole"];

        configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1
                                                                          credentialsProvider:credentialsProvider];

        [AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;

    // Retrieve the cognito ID.
    cognitoId = credentialsProvider.identityId;

    if (!cognitoId) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Identification Error"
                                                        message:@"Error on User Account."
                                                       delegate:nil
                                              cancelButtonTitle:@"OK"
                                              otherButtonTitles:nil];
        [alert show];
    }
}

-(NSString *)setFacebookToken:(NSString*)token {

    credentialsProvider.logins = @{ @(AWSCognitoLoginProviderKeyFacebook): token };
    [self setDeviceId];
    return cognitoId;
}

-(NSString *)setGooglePlusToken:(NSString*)token {
    credentialsProvider.logins = @{ @(AWSCognitoLoginProviderKeyGoogle): token };
    [self setDeviceId];
    return cognitoId;
}

@end

I get no error message and the dashboard above never shows an authenticated user. The CognitoID never changes its value. Can someone tell me where the issue is?

EDIT: Updated DeviceId.m based on comments still returns nil for cognitoId

EDIT 2: Updated DeviceId.m to replace while loop checking if BFTask was finished to Bolts method waitUntilFinished.

#import "DeviceId.h"
#import <AWSiOSSDKv2/AWSCore.h>
#import <AWSCognitoSync/Cognito.h>


@implementation DeviceId
{
    __block NSString *tempCognitoId;
}

static NSString *cognitoId;
static DeviceId *_sharedInstance;
static AWSCognitoCredentialsProvider *credentialsProvider;
static AWSServiceConfiguration *configuration;

+ (DeviceId *) sharedInstance
{
    if (!_sharedInstance)
    {
        _sharedInstance = [[DeviceId alloc] init];
    }

    return _sharedInstance;
}

- (NSString *) getDeviceId
{
    return cognitoId;
}

- (void) setDeviceId
{
    /*
     * AWS Cognito
     */

    credentialsProvider = [AWSCognitoCredentialsProvider
                                                          credentialsWithRegionType:AWSRegionUSEast1
                                                          identityPoolId:@"Identity Pool ID"];

    configuration = [AWSServiceConfiguration
                                              configurationWithRegion:AWSRegionUSEast1
                                              credentialsProvider:credentialsProvider];

    [AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;

    BFTask *taskIdentity = [[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){

        if (task.error == nil)
        {
            tempCognitoId = credentialsProvider.identityId;
            NSLog(@"cognitoId: %@", cognitoId);
        }
        else
        {
            NSLog(@"Error : %@", task.error);
        }

        return nil;
    }];

    [taskIdentity waitUntilFinished];

    cognitoId = tempCognitoId;

}

-(NSString *)setFacebookToken:(NSString*)token {

    credentialsProvider.logins = @{ @(AWSCognitoLoginProviderKeyFacebook): token };
    BFTask *taskIdentity = [[credentialsProvider refresh] continueWithBlock:^id(BFTask *task){

        if (task.error == nil)
        {
            tempCognitoId = credentialsProvider.identityId;
            NSLog(@"cognitoId: %@", tempCognitoId);
        }
        else
        {
            NSLog(@"Error : %@", task.error);
        }

        return nil;
    }];

    [taskIdentity waitUntilFinished];

    cognitoId = tempCognitoId;

    return cognitoId;
}

-(NSString *)setGooglePlusToken:(NSString*)token {
    credentialsProvider.logins = @{ @(AWSCognitoLoginProviderKeyGoogle): token };
    BFTask *taskIdentity = [[credentialsProvider getIdentityId] continueWithBlock:^id(BFTask *task){

        if (task.error == nil)
        {
            tempCognitoId = credentialsProvider.identityId;
            NSLog(@"cognitoId: %@", tempCognitoId);
        }
        else
        {
            NSLog(@"Error : %@", task.error);
        }

        return nil;
    }];

    [taskIdentity waitUntilFinished];

    cognitoId = tempCognitoId;

    return cognitoId;
}

@end

I do realize that some may consider waiting for completion is bad practice but I need to be sure for testing purposes that cognitoId is returned "synchronously". After modifying the Facebook App ID, using this method the cognitoID is returned.

Edit 3: However, Google+ is failing before reaching deviceId.

- (void)finishedWithAuth:(GTMOAuth2Authentication *)auth
               error:(NSError *)error {
    if (error) {
        _signInAuthStatus.text =
        [NSString stringWithFormat:@"Status: Authentication error: %@", error];
        NSLog(@"%@", error);
        return;
    }
    NSString *idToken = [auth.parameters objectForKey:@"id_token"];
    DeviceId *myDeviceId = [DeviceId sharedInstance];
    [myDeviceId setGooglePlusToken:idToken];
}

The NSLog error prints: Error Domain=com.google.GooglePlusPlatform Code=-1 "The operation couldn’t be completed. (com.google.HTTPStatus error 400.)" The API & Auth Product Name and Current Email Address appear to be correct on the console.

Edit 4: Cognito Synch in another section of the code has now stopped working where it worked before:

    AWSCognito *syncClient = [AWSCognito defaultCognito];
    AWSCognitoDataset *dataset = [syncClient openOrCreateDataset:@"myDataSet"];
    NSString *fullName = [dataset stringForKey:@"name"];

Fails on the first line with the error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[AWSEndpoint endpointWithRegion:service:]: unrecognized selector sent to class

Any help on these additional errors is appreciated.

Spidey
  • 696
  • 8
  • 11

2 Answers2

0

It looks like, after setting the token in the credentialsProvider.logins map, you are overriding your credentialsProvider by calling setDeviceId again. setDeviceId creates a brand new AWSCognitoCredentialsProvider, which will not have the token you added to the previous one, and overwrites the variable credentialsProvider.

  • Thanks for the comment @Albert - but I commented out the line - [self setDeviceId] - ran the app and I see no authenticated user in the dashboard. Any other ideas? Anyone? – Spidey Mar 08 '15 at 03:56
  • Are you calling setDeviceId somewhere else if you comment it out from setFacebookToken and setGooglePlusToken? There is no other place in the code you pasted that calls setDeviceId, and it should be called at least once before settings the logins, so the credentialsProvider object is initialized. – Albert Vaca Cintora Mar 09 '15 at 23:54
  • I moved the [self setDeviceId]; above the credentialsProvider.logins line and made sure there were no prior calls to setDeviceId. Still no authentication. – Spidey Mar 12 '15 at 01:23
  • @Spidey AWSCognitoCredentialsProvider uses lazy loading. Simply seeting the token on the provider does not cause authentication to occur. If you want to see the results of the authentication immediately you will need to use the credentials provider to make a call to an AWS service or call refresh explicitly. – Bob Kinney Mar 12 '15 at 16:52
  • I have modified the code to refresh the credentials provider and I'm still getting a null returned in credentialsProvider.identityId. – Spidey Mar 17 '15 at 12:02
  • What version of the iOS SDK are you using? The correct way to wait until the BFTask is finished is to use waitUntilFinished. See the make it synchronous here: http://mobile.awsblog.com/post/Tx2B17V9NSVLP3I/The-AWS-Mobile-SDK-for-iOS-How-to-use-BFTask As it is, you are sleeping for .05 s (20 ms) which is not long enough to get a response. – perpil Mar 19 '15 at 18:35
  • @behrooziAWS - I am using version 2.0.17. I will edit the code as it is reflected now using waitUntilFinished instead of waiting for the task to finish using the while loop. After making changes to the Facebook App ID the Facebook authentication works using this method. The Google+ authentication throws an error: Error Domain=com.google.GooglePlusPlatform Code=-1 "The operation couldn’t be completed. (com.google.HTTPStatus error 400.)" The API & Auth Product Name and Email Address appear to be correct in the console. – Spidey Mar 23 '15 at 23:02
  • @Spidey, does this link help? It looks to be the same error code you're seeing. http://stackoverflow.com/questions/25682745/getting-error-after-redirecting-to-ios-app-from-google-plus-signin – Jeff Bailey Mar 24 '15 at 02:12
  • @Jeff - The API & Auth Product Name and Email Address appear to be correct in the console. – Spidey Mar 24 '15 at 23:05
  • @Spidey the most common occurrence of this issue seems to be that. If you have confirmed that the product name and email addresses are entered, then I have a few more thoughts. I believe you have to re-create the OAuth credentials after entering this data, so if you hadn't, then that may be worth trying. Additionally, check if you're using the web server API key, *not* the iOS client key. – Jeff Bailey Mar 24 '15 at 23:36
  • @Jeff Bailey - sorry for the delay but we've been changing keys on the console and client - and nothing seemed to work. As it turns out, now we are authenticating correctly with both Facebook and Google+ as of this morning. Not exactly sure what did the trick at this point. If you want, answer this question and I'll give you the credit for it. I will ask the Cognito Synch question separately since there will be a different answer for it - it is still failing as shown in Edit 4. – Spidey Apr 02 '15 at 23:25
0

A common cause for the issue you described with

Error Domain=com.google.GooglePlusPlatform Code=-1 "The operation couldn’t be completed. (com.google.HTTPStatus error 400.)

Is described in this stackoverflow question. I'd double check that you have the product name and email address entered. Additionally, I believe you have to re-create the OAuth credentials after entering this data, so if you hadn't yet done that, it may be worth trying.

Finally, check that you're using the web server API key, not the iOS client key.

Community
  • 1
  • 1
Jeff Bailey
  • 5,655
  • 1
  • 22
  • 30
  • I also used the json web token website http://jwt.io/ to check the tokens returned from Facebook and Google+ to ensure consistency with our keys in the Amazon console. – Spidey Apr 08 '15 at 00:16