Annoyingly AppSync does not support multiple authentication methods, and as you've noticed Cognito UserPool integration requires users to have signed in before they can access the graphql endpoint.
There are a few workarounds, however none of them are that pretty:
- Deploy two versions- one with full functionality using UserPools and one open version using API Key (that you embed into the client)
- Handle authentication yourself. Use the IAM Role security for App Sync, and instead of connecting directly to the graphql endpoint, create a proxy lambda in API Gateway that either verifies UserPool users or allows limited access with no credentials.
- Create a no-user "user" in your UserPool with known (e.g. in your app, or from an endpoint) credentials. The onus would then be on you to check if your user is this user when responding to secured mutations/queries. One way of doing this more safely might be to add a custom attribute "custom:insecure" which you can check for on non-open endpoints.
- Use the "viewer" pattern (check Facebook's implementation). Make your GraphQL Schema open by default (e.g. known API Key) and have all of your queries/mutations take the first parameter of
viewer. This might be the access token that Cognito returns on authentication. Each resolver would then be responsible for determining if the token is valid (e.g.cognito::getUser) or access is allowed without a viewer defined.
Although it initially sounds like the hardest, I would recommend my first solution. There are ways of automating AppSync deployments, and it makes a clear distiniction between what's open and what's secured on your schema.