DEV Community

Cover image for Scaling AuthZ with Amplify
Ross Ragsdale for AWS Community Builders

Posted on β€’ Edited on β€’ Originally published at blog.rossragsdale.com

5 2

Scaling AuthZ with Amplify

AppSync is an amazing tool for building APIs. It's serverless- so you can spend more time building what matters. It also has integrations to resources like AWS Cognito and schema generation from AWS Amplify, your schema and authorizations can all live in one place.

Alt Text

Scaling with @auth

If you've ever worked with Amplify you know how great the framework is at simplifying the complexity of AWS. Amplify helps you build the latest and greatest in serverless architecture, in the simplest possible way. It's a new framework with a bright future but isn't without growing pains.

For AppSync APIs in more complex authorization schemes (multi-tenant, multi-many-to-many), using just 'owner' and 'group' settings for the @auth directive provided by Amplify starts to get tricky. You may run into situations where it's unclear how to authorize certain data without substantially altering your schema design. Well don't throw in the towel quite yet- there's still hope!

The power of AppSync IAM authorization

Want to write less VTL, more Javascript, and still get the benefits of Amplify's codegen? Then you'd be happy to find out that you can leverage AppSyncs IAM auth to do just that.

πŸ‘‡ This @auth transformer rule is pure ✨magic✨- and here's why:

@auth(
rules: [
{
allow: private
provider: iam
operations: [create, update, delete, read]
}
]
)
view raw schema.graphql hosted with ❀ by GitHub

The Proxy-Dispatcher pattern

Alt Text

The concept is simple, in place of owner and group @auth- leverage IAM auth and run queries from a @function resolver.

Consider a "simple" schema with a few many-to-many relationships...

type Organization
@model(subscriptions: null)
@auth(
rules: [
{
allow: private
provider: iam
operations: [create, update, delete, read]
}
]
) {
id: ID!
name: String
website: AWSURL
about: String
users: [Membership]
@connection(keyName: "byOrganization", fields: ["id"])
}
type User
@model
@auth(
rules: [
{
allow: owner
ownerField: "id"
operations: [create, update]
}
{
allow: private
provider: iam
operations: [create, update, delete, read]
}
]
) {
id: ID!
firstName: String!
lastName: String!
email: AWSEmail!
birthDate: AWSDateTime
organizations: [Membership]
@connection(keyName: "byUser", fields: ["id"])
meetings: [Attendance]
@connection(keyName: "byUser", fields: ["id"])
}
type Meeting
@model(subscriptions: null)
@auth(
rules: [
{
allow: private
provider: iam
operations: [create, update, delete, read]
}
]
) {
id: ID!
name: String
public: Boolean
date: AWSDateTime!
director: User @connection
manager: User @connection
publisher: User @connection
members: [Attendance]
@connection(keyName: "byCollaboration", fields: ["id"])
}
type Membership
@model(subscriptions: null)
@auth(
rules: [
{
allow: private
provider: iam
operations: [create, update, delete, read]
}
]
)
@key(name: "byUser", fields: ["userID"])
@key(name: "byOrganization", fields: ["organizationID"]) {
id: ID!
userID: ID!
user: User @connection(fields: ["userID"])
organizationID: ID!
organization: Organization @connection(fields: ["organizationID"])
}
type Attendance
@model(subscriptions: null)
@auth(
rules: [
{
allow: private
provider: iam
operations: [create, update, delete, read]
}
]
)
@key(name: "byUser", fields: ["userID"])
@key(name: "byMeeting", fields: ["meetingID"]) {
id: ID!
userID: ID!
user: User @connection(fields: ["userID"])
meetingID: ID!
meeting: Meeting @connection(fields: ["meetingID"])
}
view raw schema.graphql hosted with ❀ by GitHub

Apologies that you had to scroll through all of that. Lengthy for sure, but consider how little code is required in the schema to fully authorize it.

You may have actually noticed that a client-side application would have zero access to data with the above schema, and that's intentional. We'll be adding a few new operations for the client to use:

type Mutation {
createOrUpdateUser(input: UserInput): AWSJSON
@aws_iam @aws_cognito_user_pools
@function(name: "GraphQLResolver-${env}")
createOrUpdateOrganization(input: OrgInput): AWSJSON
@aws_iam @aws_cognito_user_pools
@function(name: "GraphQLResolver-${env}")
createOrUpdateMeeting(input: MeetingInput): AWSJSON
@aws_iam @aws_cognito_user_pools
@function(name: "GraphQLResolver-${env}")
}
type Query {
search(query: String): AWSJSON
@aws_iam @aws_cognito_user_pools
@function(name: "GraphQLResolver-${env}")
}
view raw schema.graphql hosted with ❀ by GitHub

The @aws_iam and @aws_cognito_user_pools directives are just native AppSync ones, and what @auth([{allow: private}]) would transform into, so either form works.

The final step is to deploy a @function resolver that can handle the above operations and return data to the user based on the custom business logic. That'll be for my next blog post!

Final thoughts

Not sure if this approach is for you? If you have an AppSync APIs and are managing many-to-many models, you've either battled or currently are wrestling with these scenarios. If your API is relatively simple, stick with the owner/group @auth directives provided by Amplify. They're easy to implement and will keep your app secure with little complexity overhead.

You can expect the queries to be slower, due to cold starts and the extra AppSync request. While this can help with corner cases for tough authorizations, you'll still need to have a working understanding of VTL if you want to optimize your common case queries.

Another downside to this approach is having to manually add input types to the schema to have feature parity with the generated filters and conditionals generated by Amplify. I'm currently working on a new Amplify GraphQL Transformer that will eliminate this redundant code and could open the door to some cool, new schema designs as well.

If you found this useful or would like to hear more- leave a comment, πŸ–€ and subscribe! Thanks.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

Best Practices for Running  Container WordPress on AWS (ECS, EFS, RDS, ELB) using CDK cover image

Best Practices for Running Container WordPress on AWS (ECS, EFS, RDS, ELB) using CDK

This post discusses the process of migrating a growing WordPress eShop business to AWS using AWS CDK for an easily scalable, high availability architecture. The detailed structure encompasses several pillars: Compute, Storage, Database, Cache, CDN, DNS, Security, and Backup.

Read full post

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay