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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With