Issues with Amazon Cognito iOS SDK V2 using Facebo

2019-09-14 07:54发布

问题:

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.

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.

回答1:

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.



回答2:

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.