3

So I've set up cognito and appsync and connected them both to my iOS client. Appsync works great from the console, but when i make any requests from iOS i get a 401 error without any error messages. I'm able to sign in and out of cognito fine. I think i might be passing the wrong thing in to something maybe?

Here's my app delegate code: import UIKit import AWSAppSync import AWSS3 import AWSCognitoIdentityProvider

var credentialsProvider: AWSCognitoCredentialsProvider?
var pool: AWSCognitoIdentityUserPool?

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var storyboard: UIStoryboard? {
        return UIStoryboard(name: "Main", bundle: nil)
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        AWSDDLog.sharedInstance.logLevel = .verbose
        AWSDDLog.add(AWSDDTTYLogger.sharedInstance)
        let configuration = AWSServiceConfiguration(region: AWSRegion, credentialsProvider: nil)

        let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: CognitoAppId, clientSecret: nil, poolId: CognitoPoolId)

        AWSCognitoIdentityUserPool.register(with: configuration, userPoolConfiguration: poolConfiguration, forKey: CognitoIdentityPoolId)

        pool = AWSCognitoIdentityUserPool(forKey: CognitoIdentityPoolId)

        NSLog("cognito pool username: \(pool?.currentUser()?.username ?? "unknown")")
        pool!.delegate = self

        credentialsProvider = AWSCognitoCredentialsProvider(regionType: AWSRegion, identityPoolId: CognitoIdentityPoolId, identityProviderManager: pool!)

        let databaseURL = URL(fileURLWithPath:NSTemporaryDirectory()).appendingPathComponent(database_name)

        do {
            // Initialize the AWS AppSync configuration
            let appSyncConfig = try AWSAppSyncClientConfiguration(url: AppSyncEndpointURL, serviceRegion: AWSRegion,
                                                                  credentialsProvider: credentialsProvider!,
                                                                  databaseURL:databaseURL)

            // Initialize the AppSync client
            appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)

            // Set id as the cache key for objects
            appSyncClient?.apolloClient?.cacheKeyForObject = { $0["id"] }
        }
        catch {
            NSLog("Error initializing appsync client. \(error)")
        }

        return true
    }

}

extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {

    func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
        let tabController = self.window?.rootViewController as! UITabBarController


        let loginViewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController


        DispatchQueue.main.async {
            tabController.present(loginViewController, animated: true, completion: nil)
        }

        return loginViewController
    }
}

and heres the error i'm getting:

Error body: {
  "errors" : [ {
    "message" : "Unable to parse JWT token."
  } ]
})
errorDescription: (401 unauthorized) Did not receive a successful HTTP code.

iam policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "appsync:GraphQL",
            "Resource": "*"
        }
    ]
}

IAM TRust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "us-west-2:94OBSCURED"
        }
      }
    }
  ]
}

Let me know if you need more detail.

Michael Economy
  • 608
  • 6
  • 21

3 Answers3

3

The AppSync initialization is tricky. All the AWS documentation/examples provide examples with IAM or API key as Authentication. If you set Cognito User Pools as Authentication in AppSync, then there are two things to be considered.

1) Create an extension to class like below.

extension YourClassName: AWSCognitoUserPoolsAuthProvider {
  func getLatestAuthToken() -> String {
   let pool = AWSCognitoIdentityUserPool(forKey: APP_TITLE)
   let session =  pool.currentUser()?.getSession()
   return (session?.result?.idToken?.tokenString)!
  }
}

2) Don't use credentialsProvider while initializing the AppSync. Instead, use userPoolsAuthProvider. The value for userPoolsAuthProvider is the class created in step 1. That could be self in case of the same class or the class name if it is a separate class.

let appSyncConfig = try AWSAppSyncClientConfiguration.init(url: AppSyncEndpointURL, serviceRegion: AppSyncRegion, userPoolsAuthProvider:self, databaseURL:databaseURL)

CognitoUserPools require JWT token whereas IAM requires IdentityPoolProvider. Passing a credentialsProvider means telling AppSync to use IAM as auth.

2

Ok the issue was: If you're using the code above, you need to set your appsync to authenticate via IAM (not Cognito). This will also require changes to your resolvers since the parameters passed to the identity object are different for IAM vs Cognito.

This is confusing because you're using Cognito (both a user pool and a federated identity user pool), but do NOT chose Cognito.

Michael Economy
  • 608
  • 6
  • 21
  • If you're using Cognito you need to choose Cognito as your auth rather than IAM. – Vladimir Aug 03 '18 at 08:19
  • 1
    @Vladimir no. this answer is correct. if you use Cognito Identity Pools THEN choose IAM. it cost me 10 hours of frustration. – EralpB May 21 '20 at 10:45
0

In this article, scroll down and take a look at the section entitled 'Authentication Modes'. This provides a good reference for the options available for api authorization for iOS projects when using AppSync. I found this very helpful.

https://awslabs.github.io/aws-mobile-appsync-sdk-ios/#configuration

ciara staggs
  • 329
  • 2
  • 7