4
votes

I am on xcode 11.4, Swift 4. The goal is to:

  1. sign up a new user in Cognito User Pool, and then save an associated user record using Amplify GraphQL.

  2. CRUD the user's record after signing in with Cognito User Pool.

The problem is I do not know how to associate Cognito with Amplify GraphQL. For example, in Google Firebase auth and Firestore, I would get a unique user id UID after signing up, then I would create an associated user record in Firestore with the key as this UID. Then on user signin/authentication, I can get this UID from firebase auth and find the associated record in firestore.

Currently with the AWS stack, I created a user model in schema.graphql as:

type User @model @auth(rules: [{ allow: owner, ownerField: "id", operations: [create, update, delete]}]){
    id: ID!
    firstName  : String
    lastName   : String
    handle     : String
    email      : String!
}

So that only authenticated user can create, update and delete. Next somewhere in SignUpController I create a new user:

AWSMobileClient.default().signUp( username: email
                                , password: password
                                , userAttributes: ["email": email]) { (signUpResult, error) in
    if let signUpResult = signUpResult {

        switch(signUpResult.signUpConfirmationState) {
            case .confirmed:
                self.showAlert(msg: "You already have an account. Please go back and press log in")
            case .unconfirmed:
                break 
            case .unknown:
                self.showAlert(msg: "Network error")
        }
    } else if let error = error { ... }

And then confirm the user w/ code:

AWSMobileClient.default().confirmSignUp(username: email, confirmationCode: code) { (signUpResult, error) in
    if let signUpResult = signUpResult {
        switch(signUpResult.signUpConfirmationState) {
            case .confirmed:
               // This is where I need to create an associated user account
               break
            case .unconfirmed:
                self.showAlert(title: "Error", msg: "User is not confirmed and needs verification via \(signUpResult.codeDeliveryDetails!.deliveryMedium) sent at \(signUpResult.codeDeliveryDetails!.destination!)")
            case .unknown:
                self.showAlert(title: "Error", msg: "Network error")
        }
    } else { //if let error = error {
        self.showAlert(title: "Error", msg: "Network error")
    }

Right now my solution in case .confirmed is to sign in immediately, and then fetch the user's client token via:

class CognitoPoolProvider : AWSCognitoUserPoolsAuthProviderAsync {

    /// this token may not be what you want ...
    func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void) {

        AWSMobileClient.default().getTokens { (token, error) in
            if let error = error {
                callback(nil,error)
            }
            callback(token?.accessToken?.tokenString, error)
        }
    }
}

This turns out to be the wrong solution, since the user's client token changes all the time.

Overall, this is a standard hello-world problem, and there should be a standard out of box solution provided by AWS. I search the docs and github, but cannot find a satisfactory answer.

1
The username AWSMobileClient.default().username and identityId AWSMobileClient.default().identityId should both be unique.Don
@Don what does that mean? Does it mean I can save the user with field id:String! set to AWSMobileClient.default().username?xiaolingxiao
@Don just to add to previous comment, on my iphone identityId is nil regardless of auth state, but username is unique across emails, and same across signin sessions for every email. I assume this is true in general as well? So does it make sense to use username as the id:String! parameter in model, so is this an anti-pattern in aws-amplify land.xiaolingxiao

1 Answers

2
votes

The right way is DON'T TRUST CLIENT for creating associate user information from Cognito, you have to do it at server side.

You should create a new Lambda Post Confirmation Trigger for Cognito and code it to create an associate account. You can use event.userName or create custom attribute uuid type likes custom:id to link your associate account.

Ref: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html