During this workshop, we’ll look into authentication, authorization and the integration with multiple backend data sources.
This repository contains template/boilerplate scripts and resources to be used during the workshop, including:
- Web (React 18 single-page-app)
- GraphQL API (AWS AppSync)
- User pool (Amazon Cognito)
- Booking NoSQL (Amazon DynamoDB) database
- Rooms SQL (Amazon Aurora Serverless) database
- Scripts and templates to get these running in the AWS public cloud
Requirements:
- AWS account
- Python 3.x to serve the web locally
- Postman to issue API requests directly
Optional:
- NodeJS to make changes and rebuild the web
- jq (if running
provision.sh
locally)
The script might return some errors on the first run, as it tries to clean up any leftovers from previous executions. It is safe to ignore errors during clean-up. Any errors from the "Provisioning" point should be reported.
NOTE: Consecutive executions of provision.sh
will reset the environment back to the starting point! Changes done in the AWS Console will be lost!
- Login into your AWS sandbox
- Select the
Europe (Ireland) - eu-west-1
region on the top right corner - Open the CloudShell on the lower left corner.
- Clone repository
git clone https://github.com/xavicampa/appsync-workshop.git
- Execute
provision.sh
. EmptyInstance ID
on the first run is fine, simply press Enter:
cd appsync-workshop
bash provision.sh
Errors are expected on the first run, as it tries to clean up leftovers from pervious executions. Pay attention to any errors happening after the Provisioning message
- Copy the final output of the script into your notepad for later use
This option assumes you have installed and configured the aws-cli with a default
AWS profile.
- Clone repository
git clone https://github.com/xavicampa/appsync-workshop.git
-
Ensure that the
default
AWS profile is correctly set up with credentials from your AWS sandbox account with region and output specified-
Make sure that your default profile DOES NOT point to a customer AWS account!
-
.aws/credentials
[default] aws_access_key_id = ***************** aws_secret_access_key = *********************
.aws/config
[profile default] region = eu-west-1 output = json
-
-
Execute
provision.sh
. EmptyInstance ID
on the first run is fine, simply press Enter:
cd appsync-workshop
bash provision.sh
Errors are expected on the first run, as it tries to clean up leftovers from pervious executions. Pay attention to any errors happening after the Provisioning message
- Copy the final output of the script into your notepad for later use
The file aws-exports.js
is primed during the execution of provision.sh
with pointers to the resources provisioned in your AWS account:
- If the script has run in the CloudShell (Option 1 above), you need to clone the repository locally in your machine and make sure to copy&paste the content of
web/aws-exports.js
from the CloudShell to your localweb/aws-exports.js
file - If the script has run from a local shell (Option 2 above), there's nothing to do
cd web
python -m http.server 3000
cd src
npm install
npm start
Once the web is running locally, it should then be possible to visit http://localhost:3000 in your browser.
Login into the application using the admin
, person1
and person2
credentials (specified in the output of the provision.sh
script above). You will be prompted to change password upon the first login of each identity. Use a password that you'll remember (or use a password manager), as you'll have to enter it when changing identities.
Open Postman and import the collection postman/GraphQLWorkshop.postman_collection.json
. There are two collection variables to be set
- URL: GraphQL API URL taken from the output of
provision.sh
- Authorization: copy the
XXXXX.accessToken
variable from theSession storage
of the browser after login. This variable needs to be updated when switching betweenadmin
,person1
andperson2
identities, as the token is unique per identity and session
Remember to save the variables.
This Authorization
blob is actually a JWT token. To inspect the contents, go to jwt.io and simply paste the JWT token in the left panel, the contents will then show up on the right panel. You'll see, among other fields, your unique user identifier, or subject, in the sub
claim. We'll use this in the exercises below.
The user pool contains two groups, admin
and guest
, and three identities, admin
, person1
and person2
. admin
user is member of the admin
group, person1
is member of the guest
group, person2
does not belong to any groups.
Authorization is defined so that only admin
members can manage bookings, having to specify guest, dates and room for the operations. guest
members identities can only view. Users without memberships cannot access any operations.
Test the following:
- Listing rooms and bookings as
admin
andperson1
both work - Listing rooms and bookings as
person2
does not work- Inspect Network tab in your browser's developer tool to check for the API error
- Error might be reported only in the browser's Developer Console
- Adding a booking as
admin
succeeds - Adding a booking as
person1
fails- Inspect Network tab in your browser's developer tool to check for the API error
- Error might be reported only in the browser's Developer Console
The following sections describe new requirements for the API. Use the AWS Console to make the required adjustments, remembering to apply/save your changes. Be aware of the eventual consistency characteristics of AWS, changes might take a few seconds to be effective.
Limit access to the guest
field from the Booking
type to admin
members.
- In the AWS Console, go to the AWS AppSync service, select BookingAPI and Schema
- Modify the schema adding
@aws_auth
to theguest
field in theBooking
type, it should only contain theadmin
cognito group - Save the schema
- running listBookings as
admin
succeeds when querying forguest
field - running listBookings as
person1
fails when querying forguest
field
- Inspect the GraphQL response, what's in
data
anderror
fields
Allow guest to add their own booking, without exposing guest as a parameter
- In the AWS Console, go to the AWS AppSync service, select BookingAPI and Schema
- Modify the schema to add a new mutation, similar to addBooking, with guest cognito group authorization, without guest parameter
- Save the schema
- Attach a new resolver to the new mutation from the panel on the right
- Select Update runtime and specify a Unit resolver (VTL)
- Copy the mapping templates from the addBooking mutation, but use
$ctx.identity.sub
as guest key, and save
- Adding a booking as guest succeeds
- Updates are not visible at once, one has to exit Bookings (Home, or Rooms) and load again Bookings
- Guest cannot remove bookings
Make guest bookings visible without refresh
- Modify the schema by adding the new mutation to the list of mutations that trigger the existing subscription
- Adding a booking as guest succeeds and appears without refresh
Allow guest to remove their own bookings, without exposing guest as parameter.
Summary: New mutation, guest auth, no guest parameter, use $ctx.identity.sub for authorization in resolver.
- Removing guest booking works
- Removing admin booking does not work
- Updates are not visible without refresh (fix by adding new mutation to subscription)
Display room price (per day) of bookings without issuing extra API calls.
OPTIONAL: modify frontend to display the new field. Requires NodeJS.
Add room
field to Booking
type of type Room
, add resolver and mapping template
- listBookings accepts
room
field and can return itsprice
- The new field requires
@aws_auth
to be added
Display bookings when listing rooms without issuing extra API calls.
OPTIONAL: modify frontend to display booking information the room list. Requires NodeJS.
Add bookings
field to Room
type of type Booking[]
, add resolver and mapping template
- listRooms accepts
bookings
field and can return its properties
- The new field requires
@aws_auth
to be added
Add filtering, so that listBookings
can filter results by guest
and roomid
.
Execute cleanup.sh
, provide the Instance ID
returned by the provision.sh
script when asked.
bash cleanup.sh