将 Cognito 中的用户信息与 AWS Amplify GraphQL 关联

Posted

技术标签:

【中文标题】将 Cognito 中的用户信息与 AWS Amplify GraphQL 关联【英文标题】:Associate user information from Cognito with AWS Amplify GraphQL 【发布时间】:2020-08-02 06:11:02 【问题描述】:

我使用的是 xcode 11.4,Swift 4。目标是:

    在 Cognito 用户池中注册一个新用户,然后使用 Amplify GraphQL 保存关联的用户记录。

    使用 Cognito 用户池登录后对用户的记录进行 CRUD。

问题是我不知道如何将 Cognito 与 Amplify GraphQL 关联起来。例如,在 Google Firebase auth 和 Firestore 中,我会在注册后获得一个唯一的用户 ID UID,然后我会在 Firestore 中创建一个关联的用户记录,其密钥为 UID。然后在用户登录/身份验证时,我可以从 firebase auth 获取此 UID 并在 firestore 中找到相关记录。

目前使用 AWS 堆栈,我在 schema.graphql 中创建了一个用户模型:

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

这样只有经过身份验证的用户才能创建、更新和删除。接下来在SignUpController 某处创建一个新用户:

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  ... 

然后用代码确认用户:

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")
    

目前我在case .confirmed 的解决方案是立即登录,然后通过以下方式获取用户的client token

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)
        
    

结果证明这是错误的解决方案,因为用户的客户端令牌一直在变化。

总的来说,这是一个标准的 hello-world 问题,应该有 AWS 提供的标准开箱即用解决方案。我搜索文档和 github,但找不到满意的答案。

【问题讨论】:

用户名 AWSMobileClient.default().username 和 identityId AWSMobileClient.default().identityId 都应该是唯一的。 @Don 这是什么意思?这是否意味着我可以将字段id:String! 设置为AWSMobileClient.default().username 来保存用户? @Don 只是为了添加到之前的评论中,在我的 iphone 上,identityId 是 nil,无论身份验证状态如何,但 username 在电子邮件中是唯一的,并且在每封电子邮件的登录会话中都是相同的。我认为这通常也是正确的?那么在model 中使用username 作为id:String! 参数是否有意义,所以这是aws-amplify 领域的反模式。 【参考方案1】:

正确的方法是不要信任客户端从 Cognito 创建关联用户信息,您必须在服务器端进行。

您应该为 Cognito 创建一个新的 Lambda Post Confirmation Trigger 并对其进行编码以创建一个关联账户。您可以使用event.userName 或创建自定义属性uuid 类型喜欢custom:id 来链接您的关联帐户。

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

【讨论】:

在客户端创建时有什么问题? 我们可能有多个客户端,那么我们如何确保/信任客户端应用程序之间的代码/工作流一致? --> 我们必须强制他们使用独特的工作流程,确保如果您注册了我的应用程序,您必须有关联帐户。如果客户代码注册错误/错误工作流程,然后重试并且由于我们没有关联帐户而无法使用旧帐户,会发生什么?

以上是关于将 Cognito 中的用户信息与 AWS Amplify GraphQL 关联的主要内容,如果未能解决你的问题,请参考以下文章

AWS Cognito 用户身份验证

在 DynamoDB 中保存 AWS Cognito 用户

如何通过 aws-cognito 属性将用户分组?

AWS Cognito 用户池的事件触发器对象

用于获取AWS Cognito用户池令牌的OAUTH2服务器?

通过 AWS Lambda 和 Cognito 注册用户(无服务器架构)