Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it recommended to use directives like @aws_iam for authentication on a type? #3143

Open
komaki-k opened this issue Feb 4, 2025 · 6 comments
Assignees
Labels
@auth pending-maintainer-response Issue is pending a response from the Amplify team. question Further information is requested

Comments

@komaki-k
Copy link

komaki-k commented Feb 4, 2025

Amplify CLI Version

12.10.2

Question

I am using Amplify with schema.graphql to create an AppSync API backed by DynamoDB(Gen1). I want to enable authentication from both Identity Pools and User Pools.
Here is an example of my schema.graphql

type Blog @model @auth(rules: [{ allow: private }, { allow: private, provider: iam }]) {
  id: ID!
  name: String!
  posts: [Post] @hasMany
}

type Post @model @auth(rules: [{ allow: private }, { allow: private, provider: iam }]) {
  id: ID!
  title: String!
  blog: Blog @belongsTo
  createdBy: User @hasOne
}

type Mutation {
  myCustomMutation(args: String): [CustomOutput]
  @function(name: "customFunction") 
  @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
}

type CustomOutput {
  id: ID! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
  name: String! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
  postsId: ID! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
  postTitle: String! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
}

This API is accessed from an application that uses Cognito User Pools for authentication.
The schema works as expected, but when CustomOutput has many fields, it quickly reaches the CloudFormation template size limit of 1000 KB.

The reason I define the schema this way is:

  • Amplify does not allow defining @auth directly on type CustomOutput (e.g., type CustomOutput @auth(...)).
  • If I omit @auth on CustomOutput, requests from the application result in the following error:
{
    "data": {
        "myCustomMutation": null
    },
    "errors": [
        {
            "path": [
                "myCustomMutation"
            ],
            "data": null,
            "errorType": "Unauthorized",
            "errorInfo": null,
            "locations": [
                {
                    "line": 2,
                    "column": 3,
                    "sourceName": null
                }
            ],
            "message": "Not Authorized to access CustomOutput on type Mutation"
        }
    ]
}

I found a workaround by modifying the schema as follows:

type Blog @model @auth(rules: [{ allow: private }, { allow: private, provider: iam }]) {
  id: ID!
  name: String!
  posts: [Post] @hasMany
}

type Post @model @auth(rules: [{ allow: private }, { allow: private, provider: iam }]) {
  id: ID!
  title: String!
  createdBy: User @hasOne
}

type Mutation {
  myCustomMutation(args: String): [CustomOutput]
  @function(name: "customFunction") 
  @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
}

type CustomOutput @aws_iam @aws_cognito_user_pools {
  id: ID!
  name: String!
  postsId: ID!
  postTitle: String!
}

This solution works, but I don't fully understand why.

Amplify requires @model for @auth, but AppSync allows defining @aws_iam and @aws_cognito_user_pools directly.
In general, when applying authentication to a type, is it recommended to use directives like @aws_iam?

Thank you always for your maintenance and support.

@komaki-k komaki-k added pending-triage question Further information is requested labels Feb 4, 2025
@komaki-k komaki-k changed the title Why can't @auth be defined without @model in Amplify? Is it recommended to use directives like @aws_iam for authentication on a type? Feb 4, 2025
@Siqi-Shan
Copy link
Member

Hey @komaki-k , thanks for reaching out for those questions!

For an Amplify-backed app and GraphQL API endpoint provisioned, it's recommended to use fine-grained auth (i.e. @auth directive) in the schema as it provides more options to customize auth rules. The Amplify supported @auth directive is different with AppSync native supported auth directives:

  • How are they different?
    • For Amplify @auth in schema, under the hood Amplify services will do transformations for your schema, which may involve additional types generation, resolvers generation (additional resolver function per operation), resources provisioned, etc. And after transformation is completed, transformed schema (which may be larger) + generated resources will be deployed to AppSync as an API endpoint.
    • For AppSync native auth directives, Amplify will not do additional transformations as corresponding auth functionalities are handled within AppSync services.
  • Why using AppSync native directives works?
    • As described in the previous point, Amplify will not generate additional resources or make the schema larger as auth functionalities are now handled within AppSync services, therefore no resource limit will be triggered.
    • I'm not entirely sure which resource limit of your project encountered without specific error messages though. It could be CloudFormation template body size 1MB, or AppSync GraphQL Schema document size 1MB.
  • Can I mix & match both directives from Amplify & AppSync?
    • Feasibility wise yes, but it is not recommended. Under the hood there will be overlapped and duplicated resolver functions executed and unexpected system behaviors might take place.
  • Which one is more recommended, Amplify or AppSync directives?
    • To get more granular control over auth rules, Amplify @auth is always more recommended.

In general, we always to recommend to get unified experiences of directives with @auth with Amplify support. However, we do understand that given differences of schemas and resources, certain limits might reached. Glad you could find the workout here!

And that's why we welcome you to have smoother schema management and developer experiences with Amplify Gen 2 and data! Feel free to give new GraphQL experience a try with TS entry points for schema definition, and enjoy what Amplify provides to developers.

Thanks for digging into this and please let me know if you have more questions.

@Siqi-Shan Siqi-Shan added pending-community-response Issue is pending a response from the author or community. @auth and removed pending-triage labels Feb 5, 2025
@komaki-k
Copy link
Author

komaki-k commented Feb 6, 2025

Hi @Siqi-Shan,
Thank you for your detailed response. I really appreciate the explanation.

I understand that AppSync native directives are not recommended and that using them comes with certain risks. However, I’m currently facing an issue where having multiple @auth rules on each output field (like in my CustomOutput) is significantly increasing the size of my API’s CloudFormation template.

Specifically, the CloudFormation template I’m referring to is the one generated at amplify-cfn-templates/api/cloudformation-template.json. Ideally, if there were a way to split this cloudformation-template.json, that would be the best solution, but I couldn’t find any CLI options for doing so.

Is migrating to Gen2 the only viable solution here? Or would using AppSync native directives, despite the risks, be another option?

I’d really appreciate your guidance on this. Amplify has been incredibly helpful—thank you!

@github-actions github-actions bot added pending-maintainer-response Issue is pending a response from the Amplify team. and removed pending-community-response Issue is pending a response from the author or community. labels Feb 6, 2025
@chrisbonifacio
Copy link
Member

chrisbonifacio commented Feb 6, 2025

Hi @komaki-k 👋 Adding onto what @Siqi-Shan said about autogenerated resolvers and auth logic that Amplify adds to the stack. The CustomOutput type is using field level auth with comes with some of its own caveats like needing to define and apply an auth rule with at least read level access to every required/non-nullable field. I believe each of those auth rules adds to the final template, as was suggested. That is probably why the limit was exceeded and the workaround is to simply applying a top level auth rule which tells AppSync to handle the authorization instead.

Unfortunately, I don't think top level authorization rules are supported for custom, non-model types so the field level auth rule leading to the limitation is unavoidable in this scenario. The workaround you discovered is the only one as far as I can imagine.

However, this workaround, while not typically recommended, shouldn't have any harmful consequences. Just need to keep in mind where the authorization is actually occurring.

I just checked and top level auth is still not supported in Gen 2 but also this workaround is not available due to not having access to AppSync directives in the TS schema builder. So, while it's not necessarily "the only viable solution", but it was designed specifically to address many of the pain points developers had with Gen 1 including limitations like the one you mentioned in this issue.

I'd be curious to see if a Gen 2 app runs into the same problem. If you have still have an example of the schema that failed to deploy and reached the limit, or even just the number of fields on CustomOutput that reproduced the error, please do share. I can try deploying the same schema in a Gen 2 app of mine to see if it would work.

Otherwise, if you'd like to try for yourself, please follow the Amplify Data docs shared before. It's pretty quick and easy to get up and running, especially if you're already setup with an AWS account to use Gen 1.

Let us know how it goes and, whether you run into issues or simply have questions, we and our community of developers are here to help! 🚀

@chrisbonifacio chrisbonifacio removed the pending-maintainer-response Issue is pending a response from the Amplify team. label Feb 6, 2025
@chrisbonifacio chrisbonifacio self-assigned this Feb 6, 2025
@AnilMaktala AnilMaktala added the pending-community-response Issue is pending a response from the author or community. label Feb 6, 2025
@komaki-k
Copy link
Author

komaki-k commented Feb 7, 2025

Hi @chrisbonifacio,

I really appreciate the clear and detailed explanation! It was really helpful, and I understand the issue much better now. I had been struggling with this for a while, so knowing that this is the only available solution is a relief.

I'd like to share an example of the schema that caused the issue. I can't provide the full schema, so this is just a sample. I haven't tested it in Gen 2, but I hope it might be useful as a reference in the future.

Although this is not the complete schema file, I believe the issue can be reproduced by defining relationships, adding a large number of fields to the Output type, and nesting another Output type with many fields inside it.
This approach might not be ideal, but if relationships are being set up, I think this is a realistic case.

# About 10 schema definitions with @model and @auth, each containing 2–40 fields
type Blog @model @auth(rules: [{ allow: private }, { allow: private, provider: iam }]) {
  id: ID!
  name: String!
  posts: [Post] @hasMany
  data1: [Something] @manyToMany(relationName: "something")
  data2: [Something] @hasOne
  data3: String!
  data4: String!
  data5: String!
  data6: String!
  data7: String!
}

# Around 10 mutation definitions like this
type Mutation {
  myCustomMutation(args: CustomInput): [CustomOutput]
  @function(name: "customFunction") 
  @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
}

# Around 10 CustomOutput definitions, each with 10–30+ fields
# Some fields reference other custom types due to relationships
type CustomOutput {
  id: ID! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
  name: String! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
  postsId: ID! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
  postTitle: String! @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
  data1: CustomDataOutput @auth(rules: [{ allow: private }, { allow: private, provider: iam }])
}

type CustomDataOutput {
  # Many more field definitions here
}

By the way, this is just out of curiosity, but is there a specific reason why top-level auth rules are not allowed?
Since only field-level auth is permitted, does that mean there are certain advantages to this approach?

@github-actions github-actions bot added pending-maintainer-response Issue is pending a response from the Amplify team. and removed pending-community-response Issue is pending a response from the author or community. labels Feb 7, 2025
@chrisbonifacio
Copy link
Member

chrisbonifacio commented Feb 10, 2025

Hi @komaki-k Thank you for providing more information on how to reproduce the issue 😃
I think we can work with this.

To answer your question regarding top level auth rules not being allowed on custom types, I believe it's because custom types are not implemented as DynamoDB Tables and instead exist as a server-side type safety check for data nested within a DynamoDB Table that was created by a model type in your schema. So, the custom type inherits the model type's auth rules at the top level but field level auth being separate checks of their own means they can still be defined on the schema.

I hope I said all that in a way that makes sense and helps!

I'll try and deploy a Gen 2 schema following the guidance you've shared and report back soon.

@chrisbonifacio chrisbonifacio added pending-community-response Issue is pending a response from the author or community. and removed pending-maintainer-response Issue is pending a response from the Amplify team. labels Feb 10, 2025
@komaki-k
Copy link
Author

Hi @chrisbonifacio
Thank you for your response!

I now have a much better understanding of the top-level auth rules issue. I really appreciate all the answers you've provided to my questions. My clarity on @auth has definitely increased a lot.

Thanks again for all your ongoing maintenance work!
Please let me know if you find anything related to the Gen 2 issue as well.

@github-actions github-actions bot added pending-maintainer-response Issue is pending a response from the Amplify team. and removed pending-community-response Issue is pending a response from the author or community. labels Feb 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@auth pending-maintainer-response Issue is pending a response from the Amplify team. question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants