Identity Proxy for AWS AppSync

Stephan Froede
4 min readJan 26, 2021

How to manage API Key Authentication across AppSync APIs

Objective

Using AWS AppSync for implementing a GraphQL based API which can be used by a backend tool or SDK, using a manageable service authorisation (API Key per shop user).

Target Architecture is using Cognito, Lambda, DynamoDB, and Aurora Serverless, Infrastructure build is done using GitLab CI & CDK (not with Amplify).

Problem

User authorisation is pushed through AWS AppSync APIs 1:1. Which means if you authorise a client with an API Key the API Key will be provided to all APIs on which the API depends.

Service Architecture

The client using an API Key would be able to authorise with the Shop API (where the API Key is managed), but not be able to access APIs which are used by this service, because the API Key only exists for the Shop API.

Finding a solution approach

There for different authorisation methods:

  • API Key
  • IAM Role
  • Open ID
  • Cognito

API Key and IAM Role are best suited for service like users. An API Key exists on the application level, while an IAM user is a system user (which makes them more powerful).

The authorisation travels through the stack using the http request header, and there is no obvious way to change the authorisation in the header, for example in a Velocity template. It is also questionable if injecting anything into the request header might not break with an AppSync update.

It is possible to manage IAM roles and distribute them to your user, in this cases all users are external users. IAM roles / users are system authorisations (AWS account being the system) distributing them to external users, means to open up your system for external users.

Hence API Keys, they exist only in an API, and you can extend the existing API Key management with your own app specific logic.

Authorisation in a GraphQL API

AppSync allows to specify authorisation for:

  • Types
  • Fields in Types
  • Queries
  • Mutations

Any combination of authorisation type and authorisation subject is possible, which allows a great degree of flexibility and granularity.

Fine grained authorisation is specified in the schema of each API. Which means there is no out of the box authorisation repository or management which would be able to orchestrate that.

Each API can be considered as an independent API, in general I would assume that frontend facing APIs have allow broader authorisations than backend authorisations.

Data Ownership

Using personalized authorisations also concerns data ownership. It appears that AppSync is also ownership aware (at least for Dynamo DB tables) and knows out-of-the boy who owns what (didn’t try it, but the documentation is suggesting it).

If you are not using AppSync’s standard you need to manage data ownership on your own — obviously.

Solution

AppSync allows fine grained authorisation per service, we can mix different authorisation methods in a service, we can use IAM roles, and there are Lambda resolvers.

  1. Shop API gets a Lambda resolver accessing backend end services (the Identity Proxy), resolver functions are accessing backend services using GraphQL
  2. Lambda resolver gets access on type and query level using IAM
  3. Backend services do allow IAM access per query, mutation, type (it is not necessary (and also not possible?) to specify which IAM role has access to which backend service
  4. Frontend API (Shop API) gets API key access for queries, mutations, and types
  5. API Keys are managed using a backend logic for shop users
  6. Data ownership is made possible by using the API key as a secondary key in the shop master data table (DynamoDB), or in that example as a secondary key in the user master data table.

It is possible to mix authorisation types in the Shop API (cognito and IAM). The Identity Proxy will always use IAM for data access. But data ownership for Cognito users will not work without further adjustments.

Alternate solution

I tried using Velocity templates first, but that did not work, because of the build order. The APIs are build in different CDK stacks (build steps). It is obviously possible to grant access on a service for a HTTP Data Source (data source of HTTP resolvers).

There is just one very important obstacle you need a “GraphqlApi” object to do this. If you are referencing an existing AppSync GraphQL service from another stack you only will get a “IGraphqlApi” object, and this object has no grant functions.

Which means it seems possible (didn’t try) to build an HTTP Resolver based Identity Proxy, but only within the same CDK stack.

Conclusion

The most promoted approach using AppSync is via Amplify (which is a great start for a new idea, especially with the new Admin Console). Migrating from an existing application or starting with a more complex architecture and additional authorisation needs some extra work in the backend.

So far could not see that I was able to compromise the security of the APIs (AppSync’s builtin security seems to work), but on the application level it is necessary to analyse and specify access rights with careful consideration. I’m however not sure in how far that is necessary without an Identity Proxy.

The Identity Proxy idea can be developed further, for example:

  • service plans
  • quality of service
  • usage plans

Which is not possible out of the box with AppSync, API Keys are just API Keys which no further abilities or functions other than the need to update validity, re-create, or delete them.

--

--