You are viewing the preview version of this book
Click here for the full version.

Authorization providers

Authorization is probably the most important AppSync API configuration as this defines who can call the API, and as we'll see later what queries, mutations, subscriptions, and fields they can use.

Access control is also defined in the schema using directives, but they don't allow much configuration. You can use the @aws_cognito_user_pools directive and define the user group, but you can't specify which user pool users need to authenticate to. Similarly, you can add API keys and define in the schema what fields are available using these keys, but creating and rotating them is done on the API level, separate from the schema.

AppSync allows the configuration of a default authorization mode as well as additional authorization providers. Each of these can be one of the supported provider.

Default authorization mode configured with a Cognito User Pool

With this two-layered approach, you can combine multiple providers, such as IAM and Cognito (a rather useful combination) or even add multiple user pools to a single API.

IAM configured as an additional authorization mode

AppSync supports several providers:

  • Cognito User Pool
  • IAM
  • OpenID connect
  • API key
  • Lambda

Let's see how each of them works!

Cognito User Pool

A Cognito User Pool is a managed user directory in AWS. It provides a storage for users, defines a flow for authentication, and handles things like passwords, MFA, user groups, and token refresh/revocation. New applications usually use Cognito as it is integrated into the AWS ecosystem and it handles a lot of edge cases out-of-the-box.

Cognito User Pools issue access tokens that services can consume. A logged-in user is identified by its token that it sends with every request made to AppSync.

Cognito authentication

A successful authentication returns a short-lived AccessToken:

Authentication to Cognito returns an access token
Access Token vs Refresh Token

Access Tokens are short-lived (usually 15 minutes to a few hours), while Refresh Tokens have a long expiration time (months to years). A Refresh Token can request new Access Tokens, so make sure you store it securely.

Then requests to AppSync needs to include this token in the Authorization header:

The user needs to send the access token with the GraphQL request
User Pool vs Identity Pool

Cognito User Pools return access tokens that the consuming services can validate against the user pool. Services that support User Pools (such as AppSync) do this automatically.

Cognito Identity Pools return AWS IAM credentials (Access Key ID and Secret Access Keys), so they provide direct access to the AWS environment. As a result, if you use Identity Pools you need to configure IAM authorization for AppSync.

Usually, you'll want to use User Pools and not Identity Pools.

Configuring a Cognito User Pool for an API allows the use of the @aws_cognito_user_pools and the @aws_auth directives (depending on whether there are multiple providers configured or just a single one) that can restrict access to specific groups:

type Query {
  # everybody can get themselves
  currentUser: User

  # only admins can query all users
  allUsers: [User]
  @aws_cognito_user_pools(cognito_groups: ["admin"])
}

IAM

IAM authorization is useful when the caller is an IAM identity: an IAM user or an IAM role. This is usually the case when using the Management Console, or when you want to call the API from a Lambda function or the AWS CLI.

IAM identities can have IAM policies and those policies can give them access to AWS resources. As an AppSync API is an AWS resource, a policy can give access to its queries, mutations, and subscriptions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["appsync:GraphQL"],
      "Resource": [
        "arn:aws:appsync:<region>:<accountid>:apis/<apiid>/types/Query/fields/allUsers",
      ]
    }
  ]
}

An IAM policy is necessary to give access to an API, but it might not be sufficient depending on how the API authorization is configured. If you have multiple providers, you also need to add the @aws_iam directive in the schema:

type Query {
  # everybody can get themselves
  currentUser: User

  # only admins and IAM identities can query all users
  allUsers: [User]
  @aws_cognito_user_pools(cognito_groups: ["admin"])
  @aws_iam
}
IAM permissions to call an AppSync API
AWS Signatures

Under the hood, calls with IAM credentials (Access Key ID and Secret Access Key) use an AWS signature on the HTTP request.

For example, this is an AppSync query, signed with an IAM role's credentials:

HttpRequest {
  method: 'POST',
  hostname: 'zb6sucjqnna73ncwc4juglddxi.appsync-api...',
  port: undefined,
  query: {},
  headers: {
    host: 'zb6sucjqnna73ncwc4juglddxi.appsync-api...',
    'x-amz-date': '20220215T082225Z',
    'x-amz-security-token': 'IQoJb3JpZ2luX2VjE...',
    'x-amz-content-sha256': 'baf7492d61aaacdcb...',
    authorization: 'AWS4-HMAC-SHA256 ... , Signature=21e592...'
  },
  body: '{"query":"\\nquery MyQuery {...}\\n    ", "operationName":"MyQuery"}',
  protocol: 'https:',
  path: '/graphql'
}

Notice that it has some extra headers: the x-amz-date, the x-amz-security-token, and the x-amz-content-sha256, and it also has a Signature in the authorization header. These are results of the signature process, and AWS automatically checks that the request contains all the required elements and that it was signed by an identity with sufficient permissions.

Usually, you don't need to know how the signature process works, and AWS provides tools to sign a request.

Calling AppSync from Javascript

The AWS JS SDK v3 provides all the necessary libraries to calculate the signature and sign a request. With it, you can implement Lambda fuctions that call AppSync using their roles' permissions.

First, import the libraries:

import {HttpRequest} from "@aws-sdk/protocol-http";
import {SignatureV4} from "@aws-sdk/signature-v4";
import {defaultProvider}
  from "@aws-sdk/credential-provider-node";
import fetch from "node-fetch";
import {URL} from "url";
import pkg from "@aws-crypto/sha256-js";
const {Sha256} = pkg;

// this is the AppSync API URL
const appsyncAPI =
  "https://syr5fg5g2navzh2u3emfhhlyze.appsync-api.eu-central-1.amazonaws.com/graphql";

Then create the HTTP request:

const {host, pathname} = new URL(appsyncAPI);

const request = new HttpRequest({
  body: JSON.stringify({
    // the GraphQL query
    query: `
query MyQuery {
  test_nothing
}
    `,
    // operation name
    operationName: "MyQuery",
    // it also supports variables
  }),
  headers: {
    host,
  },
  hostname: host,
  method: "POST",
  path: pathname,
});

Initialize the signer:

const signer = new SignatureV4({
  credentials: defaultProvider(),
  region: host
    .match(/^[^.]+\.appsync-api\.(?<region>[^.]+)\..*$/)
    .groups.region,
  service: "appsync",
  sha256: Sha256
});

Then generate the signed request and send it:

// generate the signed request
const signedRequest = await signer.sign(request);

// send the request
const res = await fetch(
  signedRequest.protocol +
    "//" +
    signedRequest.hostname +
    signedRequest.path,
  {
    body: signedRequest.body,
    headers: signedRequest.headers,
    method: signedRequest.method,
  }
);

// print the result
console.log(await res.json());

OpenID Connect

If you have users in a directory that supports OpenID Connect then you can configure that with AppSync. For example, Auth0 provides a user directory that is compatible with AppSync.

OpenID Connect flow

To configure an Auth0 user directory with AppSync, first create an Application:

Auth0 application on the Auth0 dashboard

Then add its domain for the AppSync API:

Configuring Auth0 for AppSync

This is all the configuration you'll need, now users authenticated on Auth0 can send GraphQL requests to the AppSync API. This requires the OAuth flow to get the ID token first:

Login flow

Then include that token to the GraphQL requests, just like with Cognito:

ID token is included in the GraphQL request

With this, a user in the Auth0 domain can log in and send requests:

The user on the Auth0 dashboard

AppSync resolvers get the information about the signed-in user:

There is more, but you've reached the end of this preview
Read this and all other chapters in full and get lifetime access to:
  • all future updates
  • full web-based access
  • PDF and Epub versions