0

I'm building a serverless web app for the first time on my free time. I have made my whole login workflow using AWS Cognito with Amplify but now I'm embarrassed.

I want like to stock user related data outside of cognito (Like Birthdate, Display username, Avatar picture URL, ...Etc.) but because I setup appsync with cognito auth I cannot create the user on sign-up time (before the user validation) in the database because of authorization issues. I'm not storing those data in cognito for 2 reason :

  1. Those fields may change later (like adding or removing one) and cognito don't allow attribute removal.
  2. From what I've read AWS Cognito don't allow reverse search of using using their attributes

I fell like it's a whole workflow problem and I'm open to suggestions but knowing that I have some user data only at sign-up time (like the Display username or Birthdate).

You can find here my AppSync graphql configuration (which is very basic for the moment)

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

type Mutation {
  createUserData(input: CreateUserDataInput!): UserData
  deleteUserData(input: DeleteUserDataInput!): UserData
  updateUserData(input: UpdateUserDataInput!): UserData
}

type Query {
  getUserData(email: String!, id: ID!): UserData
  listUserData(filter: TableUserDataFilterInput, limit: Int, nextToken: String): UserDataConnection
}

type Subscription {
  onCreateUserData(email: String, first_name: String, id: ID, last_name: String, pseudoname: String): UserData @aws_subscribe(mutations : ["createUserData"])
  onDeleteUserData(email: String, first_name: String, id: ID, last_name: String, pseudoname: String): UserData @aws_subscribe(mutations : ["deleteUserData"])
  onUpdateUserData(email: String, first_name: String, id: ID, last_name: String, pseudoname: String): UserData @aws_subscribe(mutations : ["updateUserData"])
}

type UserData {
  avatar: String
  email: String!
  first_name: String
  id: ID!
  last_name: String
  pseudoname: String
}

type UserDataConnection {
  items: [UserData]
  nextToken: String
}

enum ModelAttributeTypes {
  _null
  binary
  binarySet
  bool
  list
  map
  number
  numberSet
  string
  stringSet
}

enum ModelSortDirection {
  ASC
  DESC
}

input CreateUserDataInput {
  avatar: String
  email: String!
  first_name: String
  id: ID!
  last_name: String
  pseudoname: String
}

input DeleteUserDataInput {
  email: String!
  id: ID!
}

input ModelBooleanInput {
  attributeExists: Boolean
  attributeType: ModelAttributeTypes
  eq: Boolean
  ne: Boolean
}

input ModelFloatInput {
  attributeExists: Boolean
  attributeType: ModelAttributeTypes
  between: [Float]
  eq: Float
  ge: Float
  gt: Float
  le: Float
  lt: Float
  ne: Float
}

input ModelIDInput {
  attributeExists: Boolean
  attributeType: ModelAttributeTypes
  beginsWith: ID
  between: [ID]
  contains: ID
  eq: ID
  ge: ID
  gt: ID
  le: ID
  lt: ID
  ne: ID
  notContains: ID
  size: ModelSizeInput
}

input ModelIntInput {
  attributeExists: Boolean
  attributeType: ModelAttributeTypes
  between: [Int]
  eq: Int
  ge: Int
  gt: Int
  le: Int
  lt: Int
  ne: Int
}

input ModelSizeInput {
  between: [Int]
  eq: Int
  ge: Int
  gt: Int
  le: Int
  lt: Int
  ne: Int
}

input ModelStringInput {
  attributeExists: Boolean
  attributeType: ModelAttributeTypes
  beginsWith: String
  between: [String]
  contains: String
  eq: String
  ge: String
  gt: String
  le: String
  lt: String
  ne: String
  notContains: String
  size: ModelSizeInput
}

input TableBooleanFilterInput {
  eq: Boolean
  ne: Boolean
}

input TableFloatFilterInput {
  between: [Float]
  contains: Float
  eq: Float
  ge: Float
  gt: Float
  le: Float
  lt: Float
  ne: Float
  notContains: Float
}

input TableIDFilterInput {
  beginsWith: ID
  between: [ID]
  contains: ID
  eq: ID
  ge: ID
  gt: ID
  le: ID
  lt: ID
  ne: ID
  notContains: ID
}

input TableIntFilterInput {
  between: [Int]
  contains: Int
  eq: Int
  ge: Int
  gt: Int
  le: Int
  lt: Int
  ne: Int
  notContains: Int
}

input TableStringFilterInput {
  beginsWith: String
  between: [String]
  contains: String
  eq: String
  ge: String
  gt: String
  le: String
  lt: String
  ne: String
  notContains: String
}

input TableUserDataFilterInput {
  avatar: TableStringFilterInput
  email: TableStringFilterInput
  first_name: TableStringFilterInput
  id: TableIDFilterInput
  last_name: TableStringFilterInput
  pseudoname: TableStringFilterInput
}

input UpdateUserDataInput {
  avatar: String
  email: String!
  first_name: String
  id: ID!
  last_name: String
  pseudoname: String
}
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Biboozz
  • 1,096
  • 2
  • 9
  • 18

1 Answers1

1

Cognito supports all the OIDC standard claims which should cover a wide variety of use cases. For additional attributes that aren't covered consider serializing the data from your signup form and storing it as a single custom attribute in your userpool (if it is under the 2048 character limit). If it requires validation do so in a pre-signup lambda trigger.

To make that user data available consider creating another handler as a pre-token-generation trigger. In the handler you can deserialize the form data and add them as claims in your token for easier use. From this handler you can also push the data to your database to make it available for querying.

Andrew Gillis
  • 3,250
  • 2
  • 13
  • 15