Tag: Mobile

Using multiple authorization types with AWS AppSync GraphQL APIs

Using multiple authorization types with AWS AppSync GraphQL APIs

Written by Ionut Trestian, Min Bi, Vasuki Balasubramaniam, Karthikeyan, Manuel Iglesias, BG Yathi Raj, and Nader Dabit

Today, AWS announced that AWS AppSync now supports configuring more than one authorization type for GraphQL APIs. You can now configure a single GraphQL API to deliver private and public data. Private data requires authenticated access using authorization mechanisms such as IAM, Amazon Cognito User Pools, and OIDC. Public data does not require authenticated access and is delivered through authorization mechanisms such as API Keys.

You can also configure a single GraphQL API to deliver private data using more than one authorization type. For example, you can configure your GraphQL API to authorize some schema fields using OIDC, while other schema fields through Amazon Cognito User Pools or IAM.

AWS AppSync is a managed GraphQL service that simplifies application development. It allows you to create a flexible API to securely access, manipulate, and combine data from one or more data sources.

With today’s launch, you can configure additional authorization types while retaining the authorization settings of your existing GraphQL APIs. To ensure that there are no behavioral changes in your existing GraphQL APIs, your current authorization settings are set as the default.  You can add additional authorization types using the AWS AppSync console, AWS CLI, or AWS CloudFormation templates.

To add more authorization types using the AWS AppSync console, launch the console, choose your GraphQL API, then choose Settings and scroll to the Authorization settings. The snapshot below shows a GraphQL API configured to use API Key as the default authorization type. It also has two Amazon Cognito user pools and AWS IAM as additional authorization types.

  • To add more authorization types using the AWS CLI, see the create-graphql-api section of the AWS CLI Command Reference.
  • To add more authorization types through AWS CloudFormation, see AWS::AppSync::GraphQLApi in the AWS CloudFormation User Guide.

After configuring the authorization types for your GraphQL API, you can use schema directives to set the authorization types for one or more fields in your GraphQL schema. AWS AppSync now supports the following schema directives for authorization:

  • @aws_api_key to—A field uses API_KEY for authorization.
  • @aws_iam—A field uses AWS_IAM for authorization.
  • @aws_oidc—A field uses OPENID_CONNECT for authorization.
  • @aws_cognito_user_pools—A field uses AMAZON_COGNITO_USER_POOLS for authorization.

The following code example shows using schema directives for authorization:

schema {
    query: Query
    mutation: Mutation
}

type Query {
    getPost(id: ID): Post
    getAllPosts(): [Post]
    @aws_api_key
}

type Mutation {
    addPost(
        id: ID!
        author: String!
        title: String!
        content: String!
        url: String!
    ): Post!
}

type Post @aws_api_key @aws_iam {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
}

Assume that AWS_IAM is the default authorization type for this GraphQL schema. This means that fields without directives are protected using AWS_IAM. An example is the getPost() field in Query.

Next, look at the getAllPosts() field in Query. This field is protected using @aws_api_key, which means that you can access this field using API keys. Directives work at the field level. This means that you must give API_KEY access to the Post type as well. This can be done in two ways:

  • Mark each field in the Post type with a directive.
  • Mark the Post type itself with the @aws_api_key directive.

For this example, I chose the latter option.

Now, to restrict access to fields in the Post type, you can configure directives for individual fields, as shown below. You can add a field called restrictedContent to Post and restrict access to it by using the @aws_iam directive. With this setup, AWS_IAM authenticated requests can access restrictedContent, while requests authenticated with API keys do not have access.

type Post @aws_api_key @aws_iam {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    restrictedContent: String!
    @aws_iam
}

Amplify CLI

Amplify CLI version 1.6.8 supports adding AWS AppSync APIs configured with multiple authorization types. To add an API with mixed authorization mode, you can run the following command:

$ amplify add codegen —apiId <API_ID>

✖ Getting API details
✔ Getting API details
Successfully added API to your Amplify project
? Enter the file name pattern of graphql queries, mutations and subscriptions graphql/**/*.graphql
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
? Enter the file name for the generated code API.swift
? Do you want to generate code for your newly created GraphQL API Yes
✔ Downloaded the schema
✔ Generated GraphQL operations successfully and saved at graphql
✔ Code generated successfully and saved in file API.swift

Android & iOS client support

AWS also updated the Android and iOS clients to support multiple authorization types. You can enable multiple clients by setting the useClientDatabasePrefix flag to true. The awsconfiguration.json file is generated by the AWS AppSync console, and the Amplify CLI adds an entry in the AWS AppSync section. This is used to separate the caches used for operations such as query, mutation, and subscription.

Important: If you are an existing client, the useClientDatabasePrefix flag has a default value of false.  When you use multiple clients, setting useClientDatabasePrefix to true changes the location of the caches used by the client. You also must migrate any data within the caches to keep.

The following code examples highlight the new values in the awsconfiguration.json and the client code configurations.

awsconfiguration.json

The friendly_name illustrated here is created from a prompt from the Amplify CLI. There are four clients in this configuration that connect to the same API, except that they use different AuthMode and ClientDatabasePrefix settings.

{
  "Version": "1.0",
  "AppSync": {
    "Default": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "API_KEY",
      "ApiKey": "da2-xyz",
      "ClientDatabasePrefix": "friendly_name_API_KEY"
    },
    "friendly_name_AWS_IAM": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "AWS_IAM",
      "ClientDatabasePrefix": "friendly_name_AWS_IAM"
    },
    "friendly_name_AMAZON_COGNITO_USER_POOLS": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "AMAZON_COGNITO_USER_POOLS",
      "ClientDatabasePrefix": "friendly_name_AMAZON_COGNITO_USER_POOLS"
    },
    "friendly_name_OPENID_CONNECT": {
      "ApiUrl": "https://xyz.us-west-2.amazonaws.com/graphql",
      "Region": "us-west-2",
      "AuthMode": "OPENID_CONNECT",
      "ClientDatabasePrefix": "friendly_name_OPENID_CONNECT"
    }
  }
}

 

Android—Java

The useClientDatabasePrefix is added on the client builder, which signals to the builder that the ClientDatabasePrefix value should be used from the AWSConfiguration object (awsconfiguration.json).

AWSAppSyncClient client = AWSAppSyncClient.builder()
   .context(getApplicationContext())
   .awsConfiguration(new AWSConfiguration(getApplicationContext()))
   .useClientDatabasePrefix(true)
   .build();

iOS—Swift

The useClientDatabasePrefix is added to the AWSAppSyncCacheConfiguration, which reads the ClientDatabasePrefix value from the AWSAppSyncServiceConfig object (awsconfiguration.json).

let serviceConfig = try AWSAppSyncServiceConfig()
let cacheConfig = AWSAppSyncCacheConfiguration(useClientDatabasePrefix: true,
                                            appSyncServiceConfig: serviceConfig)
let clientConfig = AWSAppSyncClientConfiguration(appSyncServiceConfig: serviceConfig,
                                                   cacheConfiguration: cacheConfig)

let client = AWSAppSyncClient(appSyncConfig: clientConfig)

Public/private use case example

Here’s an example of how the newly introduced capabilities can be used in a client application.

Android—Java

The following code example creates a client factory to retrieve the client based on the need to operate in public (API_KEY) or private (AWS_IAM) authorization mode.

// AppSyncClientMode.java
public enum AppSyncClientMode {
    PUBLIC,
    PRIVATE
}

// ClientFactory.java
public class ClientFactory {

    private static Map<AWSAppSyncClient> CLIENTS;

    public static AWSAppSyncClient getAppSyncClient(AppSyncClientMode choice) {
        return CLIENTS[choice];
    }

    public static void initClients(final Context context) {
        AWSConfiguration awsConfigPublic = new AWSConfiguration(context);
        CLIENTS[PUBLIC] = AWSAppSyncClient.builder()
                                          .context(context)
                                          .awsConfiguration(awsConfigPublic)
                                          .useClientDatabasePrefix(true)
                                          .build();
        AWSConfiguration awsConfigPrivate = new AWSConfiguration(context);
        awsConfigPrivate.setConfiguration("friendly_name_AWS_IAM");
        CLIENTS[PRIVATE] = AWSAppSyncClient.builder()
                                           .context(context)
                                           .awsConfiguration(awsConfigPrivate)
                                           .useClientDatabasePrefix(true)
                                           .credentialsProvider(AWSMobileClient.getInstance())
                                           .build();
    }
}

This is what the usage would look like.

ClientFactory.getAppSyncClient(AppSyncClientMode.PRIVATE).query(fooQuery).enqueue(...);

iOS—Swift

The following code example creates a client factory to retrieve the client based on the need to operate in public (API_KEY) or private (AWS_IAM) authorization mode.

public enum AppSyncClientMode {
    case `public`
    case `private`
}

public class ClientFactory {
    static var clients: [AppSyncClientMode:AWSAppSyncClient] = [:]

    class func getAppSyncClient(mode: AppSyncClientMode) -> AWSAppSyncClient? {
        return clients[mode];
    }

    class func initClients() throws {
        let serviceConfigAPIKey = try AWSAppSyncServiceConfig()
        let cacheConfigAPIKey = try AWSAppSyncCacheConfiguration(useClientDatabasePrefix: true, appSyncServiceConfig: serviceConfigAPIKey)
        let clientConfigAPIKey = try AWSAppSyncClientConfiguration(appSyncServiceConfig: serviceConfigAPIKey, cacheConfiguration: cacheConfigAPIKey)
        clients[AppSyncClientMode.public] = try AWSAppSyncClient(appSyncConfig: clientConfigAPIKey)

        let serviceConfigIAM = try AWSAppSyncServiceConfig(forKey: "friendly_name_AWS_IAM")
        let cacheConfigIAM = try AWSAppSyncCacheConfiguration(useClientDatabasePrefix: true, appSyncServiceConfig: serviceConfigIAM)
        let clientConfigIAM = try AWSAppSyncClientConfiguration(appSyncServiceConfig: serviceConfigIAM,cacheConfiguration: cacheConfigIAM)
        clients[AppSyncClientMode.private] = try AWSAppSyncClient(appSyncConfig: clientConfigIAM)
    }
}

Conclusion

In this post, we showed how you can use the new multiple authorization type setting in AWS AppSync to allow separate public and private data authorization in your GraphQL API. While your current authorization settings are the default on existing GraphQL APIs, you can add additional authorization types using the AWS AppSync console, AWS CLI, or AWS CloudFormation templates.

from AWS Mobile Blog

Getting more visibility into GraphQL performance with AWS AppSync logs

Getting more visibility into GraphQL performance with AWS AppSync logs

Written by Shankar Raju, SDE at AWS & Nader Dabit, Sr. Developer Advocate at AWS.

Today, we are happy to announce that AWS AppSync now enables you to better understand the performance of your GraphQL requests and usage characteristics of your schema fields. You can easily identify resolvers with large latencies that may be the root cause of a performance issue. You can also identify the most and least frequently used fields in your schema and assess the impact of removing GraphQL fields. Offering support for these capabilities has been one of the top feature requests by our customers.

AWS AppSync is a managed GraphQL service that simplifies application development by letting you create a flexible API to securely access, manipulate, and combine data from one or more data sources. AWS AppSync now emits log events in a fully structured JSON format. This enables seamless integration with log analytics services such as Amazon CloudWatch Logs Insights and Amazon Elasticsearch Service (Amazon ES), and other log analytics solutions.

We have also added new fields to log events to increase your visibility into the performance and health of your GraphQL operations:

  • To search and analyze one or more log types, GraphQL requests, and multiple GraphQL API actions, we added new log fields (logType, requestId, and graphQLAPIId) to every log event that AWS AppSync emits.
  • To quickly identify errors and performance bottlenecks, we added new log fields to the existing request-level logs. These log fields contain information about the HTTP response status code (HTTPStatusResponseCode) and latency of a GraphQL request (latency).
  • To uniquely identify and run queries against any field in your GraphQL schema, we added new log fields to the existing field-level logs. These log fields contain information about the parent (parentType) and name (fieldName) of a GraphQL field.
  • To gain visibility into the time taken to resolve a GraphQL field, we also included the resolver ARN (resolverARN) in the tracing information of GraphQL fields in the field-level logs.

In this post, we show how you can get more visibility into the performance and health of your GraphQL operations using CloudWatch Logs Insights and Amazon ES. As a prerequisite, you must first enable field-level logging for your GraphQL API so that AWS AppSync can emit logs to CloudWatch Logs.

Analyzing your logs with CloudWatch Logs Insights

You can analyze your AWS AppSync logs with CloudWatch Logs Insights to identify performance bottlenecks and the root cause of operational issues. For example, you can find the resolvers with the maximum latency, the most (or least) frequently invoked resolvers, and the resolvers with the most errors.

There is no setup required to get started with CloudWatch Logs Insights. This is because AWS AppSync automatically emits logs into CloudWatch Logs when you enable field-level logging on your GraphQL API.

The following are examples of queries that you can run to get actionable insights into the performance and health of your GraphQL operations. For your convenience, we added these examples as sample queries in the CloudWatch Logs Insights console.

In the CloudWatch console, choose Logs, Insights, select the AWS AppSync log group for your GraphQL API, and then choose Sample queries, AWS AppSync queries.

Find top 10 GraphQL requests with maximum latency

fields requestId, latency
| filter logType = "RequestSummary"
| limit 10
| sort latency desc

Find top 10 resolvers with maximum latency

fields resolverArn, duration
| filter logType = "Tracing"
| limit 10
| sort duration desc

Find the most frequently invoked resolvers

fields ispresent(resolverArn) as isRes
| stats count() as invocationCount by resolverArn
| filter isRes and logType = "Tracing"
| limit 10
| sort invocationCount desc

Find resolvers with most errors in mapping templates

fields ispresent(resolverArn) as isRes
| stats count() as errorCount by resolverArn, logType
| filter isRes and (logType = "RequestMapping" or logType = "ResponseMapping") and fieldInError
| limit 10
| sort errorCount desc

The results of CloudWatch Logs Insights queries can be exported to CloudWatch dashboards. We added a CloudWatch dashboard template for AWS AppSync logs in the AWS Samples GitHub repository. You can import this template into CloudWatch dashboards to have continuous visibility into your GraphQL operations.

Analyzing your logs with Amazon ES

You can search, analyze, and visualize your AWS AppSync logs with Amazon ES to identify performance bottlenecks and root cause of operational issues. Not only can you identify resolvers with the maximum latency and errors, but you can also use Kibana to create dashboards with powerful visualizations. Kibana is an open source, data visualization and exploration tool available in Amazon ES.

To get started with Amazon ES:

  1. Create an Amazon ES cluster, if you don’t have one already.
  2. In the CloudWatch Logs console, select the log group for your GraphQL API.
  3. Choose Actions, Stream to Amazon Elasticsearch Service and select the Amazon ES cluster to which to stream your logs. You can also use a log filter pattern to stream a specific set of logs. The following example is the log filter pattern for streaming log events containing information about the request summary, tracing, and GraphQL execution summary for AWS AppSync logs.
{ ($.logType = "Tracing") || ($.logType = "RequestSummary") || ($.logType = "ExecutionSummary") }

You can create Kibana dashboards to help you identify performance bottlenecks and enable you to continuously monitor your GraphQL operations. For example, to debug a performance issue, start by visualizing the P90 latencies of your GraphQL requests and then drill into individual resolver latencies.

To build a Kibana dashboard containing these visualizations, use the following steps:

  1. Launch Kibana and choose Dashboard, Create new dashboard.
  2. Choose Add. For Visualization type, choose Line.
  3. For the filter pattern to search Elasticsearch indexes, use cwl*. Elasticsearch indexes logs streamed from CloudWatch Logs (including AWS AppSync logs) with a prefix of “cwl-”. To differentiate AWS AppSync logs from other CloudWatch logs sent to Amazon ES, we recommend adding an additional filter expression of graphQLAPIID.keyword=<AWS AppSync GraphQL API ID> to your search.
  4. To get GraphQL request data from AWS AppSync logs, choose Add Filter and use the filter expression logType.keyword=RequestSummary.
  5. Choose Metrics, Y-Axis. For Aggregation, choose Percentile; for Field, choose latency, and for Percents, enter a value of 90. This enables you to view GraphQL request latencies on the Y axis.
  6. Choose Buckets, X-Axis. For Aggregation, choose Date Histogram; for Field, choose @timestamp; and for Interval, choose Minute. This enables you to view GraphQL request latencies aggregated in 1-minute intervals. You can change the aggregation internal to view latencies aggregated at a coarser or finer grained time interval to match your data density.
  7. Save your widget and add it to the Kibana dashboard, as shown below:

  1. To build a widget that visualizes the P90 latency of each resolver, repeat steps 1, 2, 3, and 4 earlier. For step 4, use a filter expression of logType.keyword=Tracing to get resolver latencies from AWS AppSync Logs.
  2. Repeat step 5 using duration as the Field value and then repeat step 6.
  3. Choose Add sub-buckets, Split Series. For Sub Aggregation, use Terms and for Field, choose resolverArn.keyword. This enables you to visualize the latencies of individual resolvers.
  4. Save your widget and add it to the Kibana dashboard, as shown below:

Here’s a Kibana dashboard containing widgets for the P90 request latencies and individual resolver latencies:

Availability
The new logging capabilities are available in the following AWS Regions and you can start analyzing your logs today:

  • US East (N. Virginia)
  • US East (Ohio)
  • US West (Oregon)
  • Europe (Ireland)
  • Europe (Frankfurt)
  • Europe (London)
  • Asia Pacific (Tokyo)
  • Asia Pacific (Mumbai)
  • Asia Pacific (Seoul)
  • Asia Pacific (Sydney)
  • Asia Pacific (Singapore)

Log events emitted on May 8, 2019, or later use the new logging format. To analyze GraphQL requests before May 8, 2019, you can migrate older logs to the new format using a script available in the GitHub sample.

from AWS Mobile Blog

Use the Amplify Console with incoming webhooks to trigger deployments

Use the Amplify Console with incoming webhooks to trigger deployments

Written by Nikhil Swaminathan, Sr. Product Manager (Tech) at AWS.

The Amplify Console recently launched support for incoming webhooks. This feature enables you to use third-party applications such as Contentful and Zapier to trigger deployments in the Amplify Console without requiring a code commit.

You can use headless CMS tools such as Contentful with the Amplify Console incoming webhook feature to trigger a deployment every time content is updated—for example, when a blog author publishes a new post. Modern CMSs are headless in nature, which gives developers the freedom to develop with any technology because the content itself doesn’t have a presentation layer. Content creators get the added benefit of publishing a single instance of the content to both web and mobile devices.

In this blog post, we set up Amplify Console to deploy updates every time new content is published.

1. Create a Contentful account using the Contentful CLI, and follow the steps in the getting started guide. The CLI helps you create a Contentful account, a Contentful project (called a space) with a sample blog content model, and a starter repository that’s downloaded to your local machine.

2. After the CLI creates a Contentful space, log in to your Contentful space at the Contentful website and choose ‘Settings > API Keys’.

3. The API keys were generated when you ran the CLI. Copy the Space ID and Content Delivery API. You’ll need these to trigger content deployments.

4. Push the code to a Git repo of your choice (Amplify Console supports GitHub, BitBucket, GitLab, and CodeCommit).

Log in to the Amplify Console, connect your repo, and pick a branch. On the Build Settings page, enter the CONTENTFUL_DELIVERY_TOKEN and the CONTENTFUL_SPACE_ID into the environment variables section. These tokens are used by your app during the build to authenticate with the Contentful service. Review the changes, and choose Save and deploy. Your app builds and deploys to an amplifyapp.com URL. It should look like this:

5. Create an incoming webhook to publish content updates. Choose App Settings > Build Settings, and then choose Create webhook. This webhook enables you to trigger a build in the Amplify Console on every POST to the HTTP endpoint. After you create the webhook, copy the URL (it looks like  https://webhooks.amplify…)

6. Go back to the Contentful dashboard, and choose Settings > Webhooks. Then choose  Add Webhook. Paste the webhook URL you copied from the Amplify Console into the URL section and update the Content Type to application/json. Choose Save.

 

7. We’re now ready to trigger a new build through a content update! Go to the Content tab on Contentful and add a new entry with the following fields—Name: Deploying to Amplify Console and Content Type: Blog Post. Enter the other required fields, and choose Publish.

8. The Amplify Console will kickoff a new build with the newest post.

You can also use the incoming webhook feature to trigger builds on post-commit Git hooks, and through daily build schedulers. We hope you like this new feature – learn more about the Amplify Console at https://console.amplify.aws.

from AWS Mobile Blog

Amplify Framework announces new Amazon Aurora Serverless and GraphQL Transform features for building AWS AppSync APIs

Amplify Framework announces new Amazon Aurora Serverless and GraphQL Transform features for building AWS AppSync APIs

The Amplify Framework is an open-source project for building cloud-enabled applications. Today, we’re happy to announce new features for the GraphQL Transform library, which is part of the AWS Amplify command line interface (CLI). The GraphQL Transform library enables you to quickly deploy scalable AWS AppSync backends for your web and mobile applications. In this release, we’ve added new features to the library that allow for more flexibility and control over your API.

In addition, you can now use an existing Amazon Aurora Serverless database as a data source for your AWS AppSync GraphQL APIs when you’re building your mobile and web applications. This enables you to use the Amplify CLI to generate a GraphQL API with an auto-generated schema and resolvers that work with an existing Aurora Serverless database.

Support for Amazon Aurora Serverless

You can now generate a GraphQL API in front of an existing Aurora Serverless database.

Let’s say that you already have an existing database and you’d like to generate a GraphQL API in front of it. You can easily do so with the amplify api add-graphql-datasource command, and select your preferred data source type. Currently, only Aurora Serverless is supported, so the following steps  show how to import an Aurora Serverless MySQL database as a data source.

1. In the AWS Amplify CLI , run the following:

$ amplify api add-graphql-datasource
Using datasource: Aurora Serverless, provided by: awscloudformation

To import an Aurora Serverless MySQL database, you first have to choose the AWS Region where it exists. The CLI only provides the Regions where the Aurora Serverless data API is available.

? Provide the region in which your cluster is located: (Use arrow keys)
❯ us-east-1

3. Next, choose the appropriate cluster identifier from the list that the CLI provides.

? Select the Aurora Serverless cluster that will be used as the data source for your API (Use arrow keys)
❯ animals
  owners

4. Next, the CLI attempts to determine the appropriate secret to use automatically. If it’s not able to, it presents you with a list to select from .

5. Finally, the CLI provides a list of all of the databases that are active inside the selected cluster (this could take a few seconds). Choose the database that you’d like to import.

? Select the database to use as the data source: (Use arrow keys)
❯ Animals

At this point, the command executes and creates an AWS CloudFormation template with a schema that’s defined by the database it just parsed, as well as pre-generated resolvers for basic operations.

If the schema generation conflicts with resources that might already exist in your API schema or if the schema generated is invalid, the process fails and gives an appropriate error message. Otherwise, the schema is output in the amplify/backend/api/YOUR-API-NAME/ directory. The template is available in the amplify/backend/api/YOUR-API-NAME/stacks directory.

When the schema and resolvers look ready , run amplify push to build the template.

To learn more, see the documentation.

Improved GraphQL API operations control

In the past, rules defined in the @auth directive only protected top-level fields.

With the latest release, @connection resolvers now also protect access to connected fields based on the @auth rules that are defined on the related model type. Let’s take a look at how this works.

Take note of the Post type definition in the schema.

# schema.graphql
# Conceptually we are saying protect read access to owners. This has always protected
# top level fields.
type Post @model @auth(rules: [{ allow: owner, operations: [read, create, update, delete] }]) {
  id: ID!
  title: String
  author: User @connection(name: "UserPosts")
}
type User @model {
  id: ID!
  username: String
  posts: [Post] @connection(name: "UserPosts")
 # @connection resolvers used to bypass @auth rules defined on @models.
 # @connection resolvers now take those @auth directives into account. In this case,
 # you will only see posts where the $ctx.identity.username is the same as
 # the "owner" on the Post object.
}

With this schema, the @connection field also inherits the @auth rules that are set at the top-level field.

To learn more, see the documentation.

Authorization (@auth) directive improvement – Field level auth

Previously the @auth directive protected only the root-level query and mutation fields.

You can now use the @auth directive on individual fields, in addition to the object type definition. This means that you now have the ability to implement fine-grained access control across your API at both the top level  and the field level.

An @auth directive that you use on an @model OBJECT augments the top-level queries and mutations. An @auth directive that you use on a FIELD_DEFINITION protects that field’s resolver by comparing the identity to the source object designated through $ctx.source.

For example, you might have the following:

type User @model {
    id: ID!
    username: String
    
    # Can be used to protect @connection fields.
    # This resolver will compare the $ctx.identity to the "username" attribute on the User object (via $ctx.source in the User.posts resolver).
    # In other words, we are authorizing access to posts based on information in the user object.
    posts: [Post] @connection(name: "UserPosts") @auth(rules: [{ allow: owner, ownerField: "username" }])

    # Can also be used to protect other fields
    ssn: String @auth(rules: [{ allow: owner, ownerField: "username" }])
}
# Users may create, update, delete, get, & list at the top level if they are the
# owner of this post itself.
type Post @model @auth(rules: [{ allow: owner }]) {
    id: ID!
    title: String
    author: User @connection(name: "UserPosts")
    owner: String
}

With this schema, authorization rules are in place that allow only the owner to query for posts and ssn fields on the User type, while the id and username fields aren’t be protected by any authorization rules.

To learn more, see the documentation.

Feedback

We hope you like these new features! As always, let us know how we’re doing, and submit any requests in the Amplify Framework GitHub Repository. You can read more about AWS Amplify on the AWS Amplify website.

 

from AWS Mobile Blog

AWS AppSync real-time reference architecture

AWS AppSync real-time reference architecture

* This article was written by Ed Lima, Solutions Architect at AWS.

Mobile and web applications need to provide engaging and collaborative experiences to users, and real-time access to data is essential. Real-time technologies enable users to receive information as it happens. Use cases such as live scores, second screen, chat applications, dashboards, all sorts of collaborative applications, and many more wouldn’t be possible without bidirectional communication technologies such as the WebSocket protocol.

As your application and user base grows, implementing a real-time backend at scale becomes a complex problem to solve. You have to solve the problem of how to manage live connections, fan-out capabilities, and seamless performance as the number of connected clients increase. What if you could build and deploy scalable real-time applications without worrying about backend servers, connection management, high availability, and reliability? Also, what if you could take advantage of a highly optimized and robust GraphQL data layer to deliver your application data to your front-end app users? This would enable you to spend more time focusing on your business logic?

AWS AppSync is a GraphQL serverless backend for mobile, web, and enterprise applications. It provides a flexible, smart, and reliable data layer to access multiple data sources in your AWS account. AWS AppSync takes advantage of GraphQL subscriptions to perform real-time operations by pushing data to clients that choose to listen to specific events from the backend. This means that you can easily and effortlessly make any supported data source in AWS AppSync real time. The connection management is handled automatically by the AWS AppSync client SDK or AWS Amplify client using WebSocket as the network protocol between the client and service:

AWS AppSync takes care of the undifferentiated heavy lifting of managing your real-time backend infrastructure. However, it’s important that your application is well architected and designed in order to provide the best experience possible to your end users. For that reason, today we’re publishing our first AWS AppSync real-time reference architecture. It includes sample code that helps you understand and apply best practices to your next collaborative real-time application development project.

For a detailed explanation of how all the serverless parts of the puzzle fit together to perform different types of real-time use cases (one-to-many and/or many-to-many), as well as the GitHub repository where you can try the code yourself, see related article on OPENGraphQL.

from AWS Mobile Blog

Invoke AWS services directly from AWS AppSync

Invoke AWS services directly from AWS AppSync

* Written by Josh Kahn, Senior AWS Solutions Architect based in Chicago, IL.

AWS AppSync is a managed GraphQL service that enables developers to easily build data-driven mobile and web applications. Using a serverless backend, you can build a GraphQL API by connecting AWS AppSync to various data sources—including Amazon DynamoDB, AWS Lambda, and Amazon Elasticsearch Service. AWS AppSync added support for HTTP data sources in May 2018, which makes it easy to add legacy APIs to your GraphQL endpoints.

AWS AppSync has now been extended to support calling AWS services via HTTP data sources. In order for AWS to identify and authorize HTTP requests, they must be signed with the Signature Version 4 process. Otherwise, those requests are rejected. AWS AppSync can now calculate the signature on your behalf, based on the IAM role that’s provided as part of the HTTP data source configuration.

This means that you can call a broad array of AWS services without the need to write an intermediary Lambda function. For example, you could start execution of an AWS Step Functions state machine, retrieve a secret from AWS Secrets Manager, or list available GraphQL APIs from AWS AppSync itself from an AWS AppSync resolver.

Implementing a long-running query on AWS AppSync

To demonstrate the utility of this new feature, let’s build a GraphQL API with a long-running query. AWS AppSync limits GraphQL query execution to a maximum of 30 seconds. However, we might have a query (for example, a search) that sometimes takes up to one minute. We’re using AWS Step Functions to coordinate the long-running query across multiple steps, but you could opt to implement in other ways as well. We use AWS AppSync subscriptions to asynchronously update the client when the query is finished.

Create an HTTP data source

To start execution of the search state machine from AWS AppSync, we start by defining a new HTTP data source. We need to provide two additional components to invoke an AWS service:

  • An IAM role that can be assumed by AWS AppSync with permission to call states:StartExecution for our Step Functions state machine
  • The signing configuration

AWS AppSync currently provides support to add these components by using the AWS CLI, SDKs, and AWS CloudFormation.

First, we create a new IAM role with the following policy (be sure to note the role Amazon Resource Name (ARN)):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "states:StartExecution"
            ],
            "Resource": [
                "arn:aws:states:<REGION>:<ACCOUNT_ID>:stateMachine:aws-appsync-long-query"
            ],
            "Effect": "Allow"
        }
    ]
}

And trust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "appsync.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Next, we create the HTTP data source for AWS AppSync. To do this, create the following configuration file named http.json.

{
    "endpoint": "https://states.<REGION>.amazonaws.com/",
    "authorizationConfig": {
        "authorizationType": "AWS_IAM",
        "awsIamConfig": {
            "signingRegion": "<REGION>",
            "signingServiceName": "states" 
        }
    }
}

Then, use the AWS CLI to create the data source:

$ aws appsync create-data-source --api-id <API_ID> \
                                 --name StepFunctionHttpDataSource \
                                 --type HTTP \
                                 --http-config file:///http.json \
                                 --service-role-arn <ROLE_ARN>

Alternatively, you can create the same data source by using AWS CloudFormation:

StepFunctionsHttpDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      ApiId: !GetAtt SearchApi.ApiId
      Name: StepFunctionsHttpDataSource
      Description: Step Functions HTTP
      Type: HTTP
      # IAM role defined elsewhere in AWS CloudFormation template
      ServiceRoleArn: !GetAtt AppSyncServiceRole.Arn
      HttpConfig:
        Endpoint: !Sub https://states.${AWS::Region}.amazonaws.com/
        AuthorizationConfig:
          AuthorizationType: AWS_IAM
          AwsIamConfig:
            SigningRegion: !Ref AWS::Region
            SigningServiceName: states

With our HTTP data source defined and configured to sign requests to Step Functions, we can build our GraphQL API.

Building a GraphQL API

To enable our long running query, we submit the search request and immediately return the status of the search (PENDING), as well as a unique identifier for the query. Our client then needs to subscribe to updates for that particular query. The GraphQL schema is as follows:

type Result {
  id: ID!
  status: ResultStatus!
  listings: [String]
}

enum ResultStatus {
  PENDING
  COMPLETE
  ERROR
}

input ResultInput {
  id: ID!
  status: ResultStatus!
  listings: [String]!
}

type Query {
  # called by client to initiate long running search
  search(text: String!): Result
}

type Mutation {
  # called by backend when search is complete
  publishResult(result: ResultInput): Result
}

type Subscription {
  onSearchResult(id: ID!): [Result]
    @aws_subscribe(mutations: [ "publishResult" ])
}

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

Next, define the resolver for the search query in the AWS Management Console, AWS CLI, SDK, or AWS CloudFormation. Select the HTTP data source that was created previously and configure the request mapping template as follows:

$util.qr($ctx.stash.put("executionId", $util.autoId()))

{
  "version": "2018-05-29",
  "method": "POST",
  "resourcePath": "/",
  "params": {
    "headers": {
      "content-type": "application/x-amz-json-1.0",
      "x-amz-target":"AWSStepFunctions.StartExecution"
    },
    "body": {
      "stateMachineArn": "arn:aws:states:<REGION>:<ACCOUNT_ID>:stateMachine:aws-appsync-long-query",
      "input": "{ \"name\": \"$ctx.stash.executionId\" }"
    }
  }
}

There are a few items to note in this mapping:

  • We use the AWS AppSync $util.autoId() function to generate a unique identifier for the search query. This value is stored in the context stash and is later returned to the client. This value is also passed as an input parameter to our state machine.
  • The x-amz-target header specifies the AWS service and associated action to invoke.
  • JSON input data, such as the input to our state machine, must be double escaped.

The response mapping for this resolver immediately returns the query identifier and a PENDING status:

{
  "id": "${ctx.stash.executionId}",
  "status": "PENDING"
}

On executing the search query, AWS AppSync now starts the execution of the desired Step Functions state machine, passing the execution identifier as a parameter. The last step of the state machine is a task step that invokes a Lambda function. This function triggers an AppSync mutation. This causes the results to be published to the client through a subscription.

Implementing the long-running query and returning a result

With our AWS AppSync query now starting execution of the Step Functions state machine, we can implement the query and return a response to the client. For the purpose of demonstration, our state machine is quite simple. It waits for one minute, and then invokes the “Return Results” task, as shown in the following diagram.

You can easily update this simple state machine definition to support more complex or multistep queries, as long as the definition returns results through an AWS AppSync mutation along the way.

Generally, a GraphQL client executes a mutation of data that causes a change in a data source, which then triggers a notification of the change to subscribers. In this case, we define a Lambda function that calls the mutation to trigger a notification of the results.

Instead of including a GraphQL library, we’re using a simple HTTP POST to the AWS AppSync endpoint, passing an appropriate payload for a mutation. While our sample uses an AWS AppSync API_KEY authorization type, we recommend AWS_IAM in a production scenario, and would require signing the request as well. Using the nodejs8.10 runtime, our simple Return Result function is implemented as follows:

const PublishResultMutation = `mutation PublishResult(
    $id: ID!,
    $status: ResultStatus!,
    $listings: [String]!
  ) {
    publishResult(result: {id: $id, status: $status, listings: $listings}) {
      id
      status
      listings
    }
  }`;

const executeMutation = async(id) => {
  const mutation = {
    query: PublishResultMutation,
    operationName: 'PublishResult',
    variables: {
      id: id,
      status: 'COMPLETE',
      listings: [ "foo", "bar" ]
    },
  };

  try {
    let response = await axios({
      method: 'POST',
      url: process.env.APPSYNC_ENDPOINT,
      data: JSON.stringify(mutation),
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.APPSYNC_API_KEY,
      }
    });
    console.log(response.data);
  } catch (error) {
    console.error(`[ERROR] ${error.response.status} - ${error.response.data}`);
    throw error;
  }
};

exports.handler = async(event) => {
  // unique identifier of query is passed as event.name
  await executeMutation(event.name)
  return { message: `finished` }
}

AWS AppSync sends a notification to the client that’s subscribed for this particular query result. The client can then update appropriately.

Client implementation

Using AWS Amplify, we can implement a simple client to demonstrate the process of submitting the query and then subscribing for results using the returned identifier.

import Amplify, { API, graphqlOperation } from "aws-amplify";

// 1. Submit search query
const { data: { search } } = await API.graphql(
    graphqlOperation(searchQuery, { text: 'test' })
);
console.log(`Query ID: ${search.id}`);

// 2. Subscribe to search result
const subscription = API.graphql(
        graphqlOperation(onSearchResultSubscription, { queryId: search.id })
    ).subscribe({
        next: (result) => {
            // Stop receiving data updates from the subscription
            subscription.unsubscribe();
            console.log(result);
        }
    });

You can find a working example of this project on GitHub.

Invoking AWS services directly from AWS AppSync can simplify a number of use cases, such as implementation of a long-running query, or enabling the use of AWS AppSync as a GraphQL facade for other services. For more details, see the AWS AppSync Developer Guide. We’re eager to see how you take advantage of this new capability. Please reach out to us on the AWS AppSync Forum with any feedback.

from AWS Mobile Blog

Amplify Framework simplifies configuration for OAuth flows, the hosted UI, and AR/VR scenes for mobile and web developers

Amplify Framework simplifies configuration for OAuth flows, the hosted UI, and AR/VR scenes for mobile and web developers

Written by Kurt Kemple, Sr. Developer Advocate & Gerard Sans, Sr. Developer Advocate

The Amplify Framework is an open-source project for building cloud-enabled applications. Today, we’re happy to announce new features in the authentication , XR, storage, and API categories. It’s now possible to configure OAuth 2.0 authorization flows and enable the Amazon Cognito hosted UI from the Amplify command line interface (CLI) (part of the Amplify Framework). Previously, you had to go to the Amazon Cognito console to set this up and construct the proper application configurations manually in the web or mobile application.

For the Amplify XR category, the Amplify Framework provides a simplified configuration setup for augmented reality (AR) or virtual reality (VR) scenes that are powered by Amazon Sumerian in your mobile and web applications. The Amplify Framework enables you to automatically add authorization to your scenes from the Amplify CLI by using Amazon Cognito and IAM.

In addition, the Amplify Framework now supports setting up fine-grained CRUD-style permissions for the storage and API (REST) categories. This works for both authenticated and unauthenticated workflows.

Setting up the hosted UI and OAuth from the Amplify CLI

In this release of Amplify, you can now set up the Amazon Cognito hosted UI and OAuth from the Amplify CLI. This enables you to use Amazon Cognito user pools to set up federation between various identity providers (such as Amazon, Google, and Facebook) from the Amplify CLI. You use OAuth flows for the federation—either through the hosted UI or your application code, by using the endpoints directly.

Let’s look at setting up OAuth for Facebook in a React app. The first step is to create a new project.

Setting up the project

We will be using npx to create the React application. To learn more about npx, click here.

Use the following command to create a new React app project:

npx create-react-app my-project

Next you need to set up Amplify:

cd my-project
amplify init
npm i aws-amplify aws-amplify-react

Setting up OAuth with Facebook

After Amplify is set up, the next step is to create a Facebook app to enable authentication. For instructions on how to set up a Facebook app, see Adding Social Identity Providers to a User Pool.

Adding the authentication category with the Amplify CLI

Now that you have a Facebook app, it’s time to add authentication to your app with the Amplify CLI. Make sure to enter the values in the snippet below.

amplify add auth

// Do you want to use the default authentication and security configuration? Default configuration with Social Provider (Federation)
// How do you want end users to be able to sign up? Username
// What attributes are required for signing up? Email
// What domain name prefix you want us to create for you? <enter-a-unique-domain>
// Enter your redirect signin URI: http://localhost:3000/
// Do you want to add another redirect signin URI? No
// Enter your redirect signout URI: http://localhost:3000/
// Do you want to add another redirect signout URI? No
// Select the identity providers you want to configure for your user pool: Facebook
// Enter your Facebook App ID: <facebook_app_id>
// Enter your Facebook App Secret: <facebook_app_secret>

After the command completes, you need to run amplify push to create your Amazon Cognito user pool and get your endpoints for your hosted UI. You get the domain URL from the output of the “amplify push” command.

You need the endpoints to finish configuring your Facebook app.

To finish setting up your Facebook app go back to the documentation linked in the “Setting Up OAuth With Facebook” section and follow the steps for adding your app domain. You get the domain URL from the output of the amplify push command.

Add OAuth to React

The next step is to configure Amplify and the authentication category in your app. Update src/App.js with the following:

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import Amplify from "aws-amplify";
import { withOAuth } from "aws-amplify-react";
import aws_exports from "./aws-exports";

Amplify.configure(aws_exports);

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
          <button onClick={this.props.OAuthSignIn}>
            Sign in with Facebook
          </button>
        </header>
      </div>
    );
  }
}

export default withOAuth(App);

The withOAuth higher-order component (HOC) provides the property OAuthSignIn that takes the user to the hosted UI for authenticating. If you don’t want to use the HOC and instead want to handle sending users to the hosted UI, see the instructions in Launching the Hosted UI.

Try it out!

After you’ve updated App.js, it’s time to run the app and test the login. You should see a screen like the following when you choose the Sign in with Facebook button.

Improving the Amazon Sumerian scene setup

The XR category within Amplify Framework enables applications to use AR and VR content, and provides built-in Amazon Sumerian support.

With this release, we’ve simplified adding scenes from Amazon Sumerian. The Amplify CLI now prompts a few more questions so it can set up authorization for a scene, and add all the necessary roles and policies for you.

Adding a new scene workflow

Let’s look at the new workflow. We’re using a React app to demonstrate it.

npx create-react-app xr-app
cd xr-app
npm i aws-amplify aws-amplify-react

To initialize the new project, run the following:

amplify init

Before proceeding, we need to publish the scene and download its configuration as before by using the Amazon Sumerian console.

  1. In the Sumerian console, create or open the scene that you want to use, choose Publish dropdown from the top-right corner, and then choose Host privately.
  2. You’re then prompted with a dialog box. Choose the Publish button.
  3. Choose the Download JSON configuration button, and place the file into the src directory of xr-app.
  4. Rename the file to ‘scene1_config.json’.

We’re now ready to add the scene. Make sure that you have the name of your scene and the path to its configuration (for example: src/scene1_config.json). The Amplify CLI prompts all these settings, including a setting for if you want to allow unauthenticated users.

amplify add xr

Open the Amazon Sumerian console: https://console.aws.amazon.com/sumerian/home/start
Publish the scene you want to add.
Then download the JSON configuration to your local computer.
? Press Enter when ready.
? Provide a name for the scene: scene1
? Enter the path to the downloaded JSON configuration file for scene1: src/scene1_config.json
Adding XR to your project requires the Auth category.
Successfully added auth resource locally.
? Allow unauthenticated users to access your XR scene (y/N): Y
Successfully added resource scene1 locally

Some next steps:
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud

We’re ready to create all the resources in the cloud:

amplify push

Current Environment: dev

| Category | Resource name   | Operation | Provider plugin   |
| -------- | --------------- | --------- | ----------------- |
| Auth     | cognito         | Create    | awscloudformation |
| Xr       | scene1          | Create    | awscloudformation |
? Are you sure you want to continue? (Y/n) Y
⠹ Updating resources in the cloud. This may take a few minutes...
✔ All resources are updated in the cloud

Let’s add the scene to the new project. Open ‘src/App.js’ and replace its content with the following:

import React, { Component } from 'react';
import Amplify from 'aws-amplify';
import { SumerianScene } from 'aws-amplify-react';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

class App extends Component {
  render() {
    return (
      <div className="App">
        <SumerianScene sceneName={"scene1"} />
      </div>
    );
  }
}

export default App;

We need to make some adjustments to the styles so that our scene shows as centered for all devices and screen sizes. Open src/index.css and add the following styles at the top:

html, body {
  height: 100%;
}

Open src/App.css and replace the create-react-app default styles with the following:

.App {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

To try out the scene, we can run our project with the following:

npm start

Check the final output at http://localhost:3000. The following image is only for demonstration purposes.

We hope you like these new features! As always, let us know how we’re doing, and submit any requests in the Amplify Framework GitHub Repository. You can read more about AWS Amplify on the AWS Amplify website.

 

 

 

 

 

 

from AWS Mobile Blog