federatedSignIn Works for REST But Not GraphQL API
Describe the bug
Using AWSMobileClient.federatedSignIn works when using a REST API (API Gateway / Lambdas) but not when using a GraphQL API (AppSync / Datastore). For some reason when using GraphQL it doesn't send the authorization properly or is that AppSync does not recognize federated identities?
I've tried the "web sign in" but not only does it visually not look proper for a native iOS app. But it is causing no end in grief to implement in our existing system. For instance the cognitoAuthenticationProvider values are so different than when using federated identities.
Steps To Reproduce
Sign in using a native federated sign in (in our example we use Sign in With Apple) using the following code...
@objc func signIn (withToken token: String, andGivenName givenName: String, andFamilyName familyName: String, success: @escaping ()->(), failure: @escaping (_ error: NSError?)->())
{
print ("Signing in to Cognito...")
guard
let plugin = try? Amplify.Auth.getPlugin(for: AWSCognitoAuthPlugin().key),
let authPlugin = plugin as? AWSCognitoAuthPlugin,
case .awsMobileClient (let client) = authPlugin.getEscapeHatch()
else
{
failure (nil); // error
return
}
client.federatedSignIn(providerName: IdentityProvider.apple.rawValue, token: token) { (state, error) in
if let unwrappedError = error
{
print (unwrappedError)
failure (nil)
}
else if let unwrappedState = state
{
print ("Successful federated sign in:", unwrappedState)
success ();
}
}
}
Then make a call to the REST API using the following code :
@objc func doGet (path:String, success: @escaping (_ result: String)->(), failure: @escaping (_ error: NSError)->())
{
let request = RESTRequest (path: path)
Amplify.API.get (request:request)
{
result in switch result
{
case .success(let data):
self.handleSuccess(data: data, success: success)
case .failure(let apiError):
self.handleFailure(apiError: apiError, failure: failure)
}
}
}
This will work perfectly.
Then test using the GraphQL call with the following code :
@objc func getAccount (id:String, success: @escaping (_ result : OCAccount)->(), failure: @escaping (_ error: NSError)->()) {
let account = Account.keys
let predicate = account.id == id
Amplify.API.query(request: .paginatedList(Account.self, where: predicate, limit: 1000)) { event in
switch event {
case .success(let result):
switch result {
case .success(let account):
let theAccount = account[0];
print("Successfully retrieved account : \(theAccount)")
success (self.ocAccountFrom(account: theAccount));
case .failure(let error):
print("Got failed result with \(error.errorDescription)")
let objcError : NSError = NSError.init(domain: kESErrorDomain, code: -1, userInfo: ["errorDescription": error.errorDescription])
failure (objcError);
}
case .failure(let error):
print("Got failed event with error \(error)")
let objcError : NSError = NSError.init(domain: kESErrorDomain, code: -1, userInfo: ["errorDescription": error.errorDescription])
print ("Underlying Error : \(error.underlyingError)")
failure (objcError);
}
}
}
This will result in an error. See below...
### Expected behavior
Using the same authentication procedure sure work for REST and GraphQL APIs.
For some reason a GraphQL has it's own Auth setup when you create the API with `amplify add api` and select `GraphQL`. Whereas REST uses what is setup when you call `amplify add auth`. Is this the issue?
### Amplify Framework Version
1.16.1
### Amplify Categories
API, Auth
### Dependency manager
Cocoapods
### Swift version
5.0
### CLI version
7.6.26
### Xcode version
13.2.1
### Relevant log output
```shell
[API] Starting query AA2649AE-33DA-4BD2-B231-9FAFC3329904
[API] {
"variables" : {
"limit" : 1000,
"filter" : {
"id" : {
"eq" : "4bc2b6cf-815b-42fb-95f4-cb0730aced0e"
}
}
},
"query" : "query ListAccounts($filter: ModelAccountFilterInput, $limit: Int) {\n listAccounts(filter: $filter, limit: $limit) {\n items {\n id\n createdAt\n email\n familyName\n givenName\n owner\n updatedAt\n __typename\n }\n nextToken\n }\n}"
}
[API] Starting network task for query AA2649AE-33DA-4BD2-B231-9FAFC3329904
Got failed event with error APIError: The HTTP response status code is [401].
Recovery suggestion: The metadata associated with the response is contained in the HTTPURLResponse.
For more information on HTTP status codes, take a look at
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
Underlying Error : nil
Is this a regression?
No
Regression additional context
No response
Device
iPhone 12 Pro
iOS Version
iOS 15
Specific to simulators
No response
Additional context
For clarity, here is the version of all the PODS according to the Podfile.lock file...
PODS:
- Amplify (1.16.1):
- Amplify/Default (= 1.16.1)
- Amplify/Default (1.16.1)
- AmplifyPlugins/AWSAPIPlugin (1.16.1):
- AppSyncRealTimeClient (~> 1.4)
- AWSCore (~> 2.26.5)
- AWSPluginsCore (= 1.16.1)
- AmplifyPlugins/AWSCognitoAuthPlugin (1.16.1):
- AWSAuthCore (~> 2.26.5)
- AWSCognitoIdentityProvider (~> 2.26.5)
- AWSCognitoIdentityProviderASF (~> 2.26.5)
- AWSCore (~> 2.26.5)
- AWSMobileClient (~> 2.26.5)
- AWSPluginsCore (= 1.16.1)
- AmplifyPlugins/AWSDataStorePlugin (1.16.1):
- AWSCore (~> 2.26.5)
- AWSPluginsCore (= 1.16.1)
- SQLite.swift (= 0.12.2)
- AmplifyPlugins/AWSPinpointAnalyticsPlugin (1.16.1):
- AWSCore (~> 2.26.5)
- AWSPinpoint (~> 2.26.5)
- AWSPluginsCore (= 1.16.1)
- AppSyncRealTimeClient (1.6.0):
- Starscream (~> 3.1.1)
- AWSAuthCore (2.26.5):
- AWSCore (= 2.26.5)
- AWSCognitoIdentityProvider (2.26.5):
- AWSCognitoIdentityProviderASF (= 2.26.5)
- AWSCore (= 2.26.5)
- AWSCognitoIdentityProviderASF (2.26.5)
- AWSCore (2.26.5)
- AWSMobileClient (2.26.5):
- AWSAuthCore (= 2.26.5)
- AWSCognitoIdentityProvider (= 2.26.5)
- AWSCognitoIdentityProviderASF (= 2.26.5)
- AWSCore (= 2.26.5)
- AWSPinpoint (2.26.5):
- AWSCore (= 2.26.5)
- AWSPluginsCore (1.16.1):
- Amplify (= 1.16.1)
- AWSCore (~> 2.26.5)
- CocoaAsyncSocket (7.6.5)
- DTFoundation/Core (1.7.18)
- DTFoundation/DTASN1 (1.7.18):
- DTFoundation/Core
- SAMKeychain (1.5.3)
- SQLite.swift (0.12.2):
- SQLite.swift/standard (= 0.12.2)
- SQLite.swift/standard (0.12.2)
- Starscream (3.1.1)
- TCMobileProvision (1.0.0):
- DTFoundation/DTASN1 (~> 1.6)
DEPENDENCIES:
- Amplify
- AmplifyPlugins/AWSAPIPlugin
- AmplifyPlugins/AWSCognitoAuthPlugin
- AmplifyPlugins/AWSDataStorePlugin
- AmplifyPlugins/AWSPinpointAnalyticsPlugin
- CocoaAsyncSocket
- SAMKeychain
- TCMobileProvision
SPEC REPOS: trunk: - Amplify - AmplifyPlugins - AppSyncRealTimeClient - AWSAuthCore - AWSCognitoIdentityProvider
Hi @Etep15 , how did you provision the AppSync endpoint with IAM auth? Can you provide us some steps such as the selections you took after amplify add api? What's the resulting schema before amplify push (simplified/redacted if possible)? What version of the CLI are you using? We'll have to reproduce to see why the authenticated user is not able to perform that operation
Thanks for the reply @lawmicha ... Hopefully this helps below...
1. AppSync provisioning with amplify CLI
I've tried both methods, Cognito User Pools and IAM. But both result in the same. I've also set it up so both are available for use... The last time I updated the API I had Cognito User Pools as the default. So I've gone through and setup IAM as the default and tried again and had the same result on my iOS app... Here is that setup with IAM being set as the default and Cognito User Pools as secondary...
amplify api update
? Select from one of the below mentioned services: GraphQL
General information
- Name: MyProject
- API endpoint: https://[REDACTED].amazonaws.com/graphql
Authorization modes
- Default: Amazon Cognito User Pool
Conflict detection (required for DataStore)
- Conflict resolution strategy: Auto Merge
? Select a setting to edit Authorization modes
? Choose the default authorization type for the API IAM
? Configure additional auth types? Yes
? Choose the additional authorization types you want to configure for the API Amazon Cognito User Pool
Cognito UserPool configuration
Use a Cognito user pool configured as a part of this project.
GraphQL schema compiled successfully.
Edit your schema at /Users/peter/Development/xCode/MyProject/amplify/backend/api/myproject/schema.graphql or place .graphql files in a directory at /Users/peter/Development/xCode/MyProject/amplify/backend/api/myproject/schema
✅ Successfully updated resource
2. Amplify Push
I then did an amplify push which showed an "Update" for my API and I answered the following questions as follows...
? Are you sure you want to continue? Yes
GraphQL schema compiled successfully.
Edit your schema at /Users/peter/Development/xCode/MyProject/amplify/backend/api/myproject/schema.graphql or place .graphql files in a directory at /Users/peter/Development/xCode/MyProject/amplify/backend/api/myproject/schema
⠧ Building resource api/myproject schema compiled successfully.
Edit your schema at /Users/peter/Development/xCode/MyProject/amplify/backend/api/myproject/schema.graphql or place .graphql files in a directory at /Users/peter/Development/xCode/MyProject/amplify/backend/api/myproject/schema
? Do you want to update code for your updated GraphQL API Yes
? Do you want to generate GraphQL statements (queries, mutations and subscription) based on your schema types?
This will overwrite your current graphql queries, mutations and subscriptions Yes
⠦ Updating resources in the cloud. This may take a few minutes...
Once the push was completed I then tested again with that setup. Same log result as originally posted.
3. GraphQL Schema
Here is the schema. I grabbed this during the push but to be honest, it didn't change at all. as I know it well.
type Account @model @auth(rules: [{allow: owner}, {allow: private, operations: [read]}]) {
id: ID!
email: String
givenName: String
familyName: String
owner: String
# identifyingPerson: Person @hasOne # Had to remove bidirectional relationship due to Amplify Bug.
identifyingPerson: [Person] @hasMany
managingPersons: [Person] @manyToMany(relationName:"accountHasPerson")
devices: [Device] @hasMany
}
type Person @model @auth(rules: [{allow: owner}, {allow: private, operations: [read]}]) {
id: ID!
email: String @auth(rules: [{allow: owner}])
givenName: String
familyName: String
identifyingAccount: Account @belongsTo
managingAccounts: [Account] @manyToMany(relationName:"accountHasPerson")
tempForCompetition: Competition @belongsTo
tempInviteEmail: String
}
type Device @model @auth(rules: [{allow: owner}]) {
id: ID!
name: String
pushToken: String
voipToken: String
production: Boolean!
appVersion: Float
appBuild: Int
account: Account @belongsTo
}
type Competition @model @auth(rules: [{allow: owner}]) {
id: ID!
tempPersons: [Person] @hasMany
}
4. Amplify CLI Version
Finally, the Amplify CLI version is 7.6.26 which I noted in my original post so I'm hoping I'm grabbing the right number? (from amplify version)
Let me know if there is anything else needed. Much appreciate the help here!
Latest Schema (From Discord)
type Account @model @auth(rules: [{allow: owner}, {allow: private, operations: [read]}]) {
id: ID!
email: String
givenName: String
familyName: String
owner: String
# identifyingPerson: Person @hasOne # Had to remove bidirectional relationship due to Amplify Bug.
identifyingPerson: [Person] @hasMany @index (name:"byIdentifyingPerson", queryField:"accountByIdentifyingPerson")
managingPersons: [Person] @manyToMany(relationName:"accountHasPerson")
devices: [Device] @hasMany
}
type Person @model @auth(rules: [{allow: owner}, {allow: private, operations: [read]}]) {
id: ID!
email: String @auth(rules: [{allow: owner}])
givenName: String
familyName: String
identifyingAccount: Account @belongsTo @index (name:"byIdentifyingAccount", queryField:"personByIdentifyingAccount")
managingAccounts: [Account] @manyToMany(relationName:"accountHasPerson")
tempForCompetition: Competition @belongsTo
tempInviteEmail: String
}
type Device @model @auth(rules: [{allow: owner}]) {
id: ID!
name: String
pushToken: String
voipToken: String
production: Boolean!
appVersion: Float
appBuild: Int
account: Account @belongsTo #This may be unnecessary (and a security risk) as it is already owned by "Owner"
}
type Competition @model @auth(rules: [{allow: owner}]) {
id: ID!
tempPersons: [Person] @hasMany
}
Apologize for losing track on this issue. @Etep15 are you still facing this or did you find a work around?