Dynamodb, Graphql (server), Authorization and Permissions - how to get it right?

I’m new to Serverless/AWS and will appreciate some advice about pros/cons of storing “users” data in Dynamodb vs. AWS Cognito and about handling security/permissions properly.

AFAIK, we can define a DynamoDB table in serverless.yaml or in our lambda function code and libs like Dynamoose or Dynogels can help with Models/Schema and queries.

Or we can also create Dynamodb tables via AWS UI and store our users data in AWS Cognito tables.

Say, I want to query several tables (and access few users’ info) and return a composite Graphql response via single API endpoint for all queries and mutations (thus, making less requests for data, e.g. users + posts + comments + votes…). So…

  1. How can/should lambda code access a Cognito table to get a list of relevant Users (i.e. names, uuids etc.)?
  2. Pros/Cons of storing User-related data (passwords, settings) in Cognito vs. custom Users Table?
  3. How to handle per document permissions properly?

Particularly I’m working on a social/forum app that will:

  • let users create profiles and make posts and comments and follow some posts, tags, other users’s activity.
  • let users edit only their own content/settings (but admins/moderators can edit for other users)
  • let users search and filter through posts.

2 Likes
  1. Using the Cognito API https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentity.html. When fronted by ApiGateway, the cognito authorizer will provide you a lot of this information right in the request payload
  2. You should use Cognito probably. You get ‘free’ user federation with openID providers
  3. Once you use cognito, you can use DynamoDB item permissions https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html
1 Like

Thank you. I’m sorry, it wasn’t clear in my original post, I’d like to get/return other users’ info (usernames, uuids, avatars, online status etc) and can’t figure how to do it from inside lambda function (i.e. access Cognito for other users’ info).

Correct me please, but I think Cognito only passes info about current user (i.e. the one making the request), no? So how can I get this user’s “friends” info (or show a list of voters for a post)?

Does it make sense to store users’ emails, passwords and UUIDs in Cognito and then duplicate UUIDs, usernames, emails in Dynamodb in User-Profile table along with user’s preferences/settings?
(so lambda can access all the necessary info to send a Graphql response, or act upon those settings, e.g. send notifications).

Is there a better way to arrange such info for a forum-like app, considering there will be events/triggers like “Hey! Alice and Bob like your post” ?

Have you looked at this API Action (ListUsers - Lists the users in the Amazon Cognito user pool - returns the attributes):
http://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListUsers.html

1 Like

I always try to use Cognito for registration/validation/recovery process, saving just email/password on Cognito, and then save all the rest on dynamo .- An example on how to use cognito that way Building a public API on AWS..

Just in case it helps :slight_smile:

1 Like

Thanks! So far, it seems I should use Cognito for authentication and keep/duplicate some user info in Dynamodb tables as it is easier to construct queries to Dynamodb tables using Dynamoose/Dynogels.

Can someone point me to an article or source code for an app (using AWS) that implements similar functionality as described above? (I already went through the Serverless-stack tutorial)

like: SpaceFinder (by AWS Labs) or Serverless-Graphql-API (using Apollo)

Just to chime in, we use Cognito strictly for authentication and store all other user data in DynamoDB (using their emails as ids).

There is a way for DynamoDB to prevent access using an ID from a Cognito Identity Pool User. You’d need to add the following policy to the role your users will take when coming in from Cognito (two roles if you have auth AND unauth):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect":"Allow",
      "Action": [
        "dynamodb:BatchWriteItem",
        "dynamodb:DeleteItem",
        "dynamodb:PutItem",
        "dynamodb:UpdateItem"
      ],
      "Resource": [
        "<TABLE_ARN>"
      ],
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
        }
      }
    },
    {
      "Effect":"Allow",
      "Action": [
        "dynamodb:BatchGetItem",
        "dynamodb:DescribeTable",
        "dynamodb:GetItem",
        "dynamodb:ListTables",
        "dynamodb:Query"
      ],
      "Resource": [
        "<TABLE_ARN>"
      ],
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
        }
      }
    }
  ]
}

1 Like

Thanks! Can you explain why you guys decided to use emails as IDs, instead of some randomly generated UUID?
What happens if some users change their emails? How do they keep their account related info/profile?

Technically we’re not using their emails, we’re using the sub claim in the JWT token (which cannot be changed after registration). We set the sub claim to be the email, and we do not allow changing emails in our application (it’s for internal company use).

1 Like