18 | The app is what makes this service exist, but the unicorns make it move. Meet them and see who you are riding with!
19 |
20 |
21 |
30 |
31 |
32 |
33 |
34 | Wild Rydes has a dedicated staff that recruits, trains, and tends to our herd of unicorns. We take great pride in the quality of unicorns and rydes that we provide to our customers, and our staff exercises the utmost care in vetting the unicorns that join our herd.
35 |
36 |
37 | Every unicorn goes through a rigorous due diligence process where we perform background checks, flying exams, and several rounds of interviews. Unicorns accepted to Wild Rydes are then treated to the best care and maintenance possible. We provide them excellent benefits, health care, and employee perks. This is part of our company philosophy in which happy unicorns lead to happy customers.
38 |
39 |
Meet a few of the unicorns that are part of our family.
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Bucephalus
49 |
Golden Swiss
50 |
51 | Bucephalus joined Wild Rydes in February 2016 and has been giving rydes almost daily. He says he most enjoys getting to know each of his ryders, which makes the job more interesting for him. In his spare time, Bucephalus enjoys watching sunsets and playing Pokemon Go.
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
Shadowfox
63 |
Brown Jersey
64 |
65 | Shadowfox joined Wild Rydes after completing a distinguished career in the military, where he toured the world in many critical missions. Shadowfox enjoys impressing his ryders with magic tricks that he learned from his previous owner.
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
Rocinante
77 |
Baby Flying Yellowback
78 |
79 | Rocinante recently joined the Wild Rydes team in Madrid, Spain. She was instrumental in forming Wild Rydes’ Spanish operations after a long, distinguished acting career in windmill shadow-jousting.
80 |
81 |
82 |
83 |
84 |
85 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/resources/code/website/server/requestUnicorn.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | const express = require('express');
16 | const bodyParser = require('body-parser');
17 | const AWS = require('aws-sdk');
18 | const awssem = require('aws-serverless-express/middleware');
19 | const randomBytes = require('crypto').randomBytes;
20 |
21 | // Configure the appropriate region for the app
22 | const region = process.env.MOBILE_HUB_PROJECT_REGION || process.env.REGION || 'us-east-1';
23 | AWS.config.update({ region: region })
24 | let databaseTableName = 'Rides';
25 | if (process.env.MOBILE_HUB_DYNAMIC_PREFIX) {
26 | databaseTableName = process.env.MOBILE_HUB_DYNAMIC_PREFIX + '-Rides';
27 | }
28 |
29 | // In a real app, this would be placed into a database table
30 | const fleet = [
31 | { Name: 'Bucephalus', Color: 'Golden', Gender: 'Male' },
32 | { Name: 'Shadowfax', Color: 'White', Gender: 'Male' },
33 | { Name: 'Rocinante', Color: 'Yellow', Gender: 'Female'}
34 | ];
35 |
36 | // declare a new express app
37 | var app = express()
38 | app.use(awssem.eventContext({ deleteHeaders: false }));
39 | app.use(bodyParser.json());
40 | app.use(function (req, res, next) {
41 | res.header("Access-Control-Allow-Origin", "*")
42 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
43 | next()
44 | });
45 |
46 | // Create a DynamoDb connection
47 | const ddb = new AWS.DynamoDB.DocumentClient();
48 |
49 | // Respond to POST /requestUnicorn
50 | app.post('/ride', function(req, res) {
51 | const requestContext = req.apiGateway.event.requestContext;
52 |
53 | const rideId = randomBytes(16)
54 | .toString('base64')
55 | .replace(/\+/g, '-')
56 | .replace(/\//g, '_')
57 | .replace(/=/g, '');
58 |
59 | const username = requestContext.identity.cognitoIdentityId;
60 | const pickupLocation = req.body.PickupLocation;
61 |
62 | // Select a unicorn from the fleet. A more normal implementation
63 | // will use a routing algorithm to select from a database.
64 | const unicorn = fleet[Math.floor(Math.random() * fleet.length)];
65 |
66 | // This is the item we will insert into the database
67 | const ddbItem = {
68 | RideId: rideId,
69 | User: username,
70 | Unicorn: unicorn,
71 | UnicornName: unicorn.Name,
72 | RequestTime: new Date().toISOString(),
73 | PickupLocation: pickupLocation
74 | };
75 |
76 | console.log(`Inserting data into ${region}:${databaseTableName}`);
77 | ddb.put({ TableName: databaseTableName, Item: ddbItem },
78 | function (err, data) {
79 | if (err) {
80 | console.log('error: ', err);
81 | res.status(500).json({
82 | Error: err.message,
83 | Reference: req.requestId
84 | });
85 | } else {
86 | console.log('success: ', data);
87 | res.status(201).json({
88 | RideId: rideId,
89 | Unicorn: unicorn,
90 | UnicornName: unicorn.Name,
91 | Eta: 30,
92 | Rider: username
93 | });
94 | }
95 | }
96 | );
97 | });
98 |
99 | // Work the local server proxy so that it listens on port 3000
100 | // Note that the yarn start server also runs on port 3000, so
101 | // you can't run them at the same time.
102 | app.listen(3000, function() {
103 | console.log("App started")
104 | });
105 |
106 | // Export the app object. When executing the application local this
107 | // does nothing. However, to port it to AWS Lambda we will create a
108 | // wrapper around that will load the app from this file
109 | module.exports = app
110 |
--------------------------------------------------------------------------------
/resources/code/website/src/amplify-config.js:
--------------------------------------------------------------------------------
1 | // This file is used for manual configuration of the Amplify library.
2 | // When Amplify is used in conjunction with the Amplify CLI toolchain or AWS Mobile Hub to manage backend resources,
3 | // an aws-exports.js file is auto-generated and can be used instead of the below to automatically configure the Amplify library.
4 | // In this workshop, we are using the Amplify client libraries without the CLI toolchain so you should edit this file manually.
5 |
6 | const awsConfig = {
7 | Auth: {
8 | identityPoolId: '', // example: 'us-east-2:c85f3c18-05fd-4bb5-8fd1-e77e7627a99e'
9 | region: '', // example: 'us-east-2'
10 | userPoolId: '', // example: 'us-east-2_teEUQbkUh'
11 | userPoolWebClientId: '' // example: '3k09ptd8kn8qk2hpk07qopr86'
12 | },
13 | API: {
14 | endpoints: [
15 | {
16 | name: 'WildRydesAPI',
17 | endpoint: '', // example: 'https://u8swuvl00f.execute-api.us-east-2.amazonaws.com/prod'
18 | region: '' // example: 'us-east-2'
19 | }
20 | ]
21 | },
22 | Storage: {
23 | bucket: '', //example: 'wildrydesbackend-profilepicturesbucket-1wgssc97ekdph'
24 | region: '' // example: 'us-east-2'
25 | }
26 | }
27 |
28 | export default awsConfig;
29 |
--------------------------------------------------------------------------------
/resources/code/website/src/auth/SignIn.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import { Auth } from 'aws-amplify';
17 | import DynamicImage from '../components/DynamicImage';
18 | //import { withRouter } from 'react-router-dom';
19 | import { useLocation, useNavigate, useParams } from 'react-router-dom';
20 |
21 | import '../css/app.css';
22 |
23 | /**
24 | * Sign-in Page
25 | */
26 | class SignIn extends React.Component {
27 | constructor(props) {
28 | super(props);
29 | this.state = {
30 | stage: 0,
31 | email: '',
32 | password: '',
33 | code: '',
34 | userObject: null
35 | };
36 | }
37 |
38 | async onSubmitForm(e) {
39 | e.preventDefault();
40 | console.log('Form Submitted');
41 | this.setState({ stage: 1 });
42 | }
43 |
44 | async onSubmitVerification(e) {
45 | e.preventDefault();
46 | console.log('Verification Submitted');
47 | this.setState({ stage: 0, email: '', password: '', code: '' });
48 | // Go back home
49 | this.props.history.replace('/');
50 | }
51 |
52 |
53 | onEmailChanged(e) {
54 | this.setState({ email: e.target.value.toLowerCase() });
55 | }
56 |
57 | onPasswordChanged(e) {
58 | this.setState({ password: e.target.value });
59 | }
60 |
61 | onCodeChanged(e) {
62 | this.setState({ code: e.target.value });
63 | }
64 |
65 | isValidEmail(email) {
66 | var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
67 | return re.test(String(email).toLowerCase());
68 | }
69 |
70 | renderSignIn() {
71 | const isValidEmail = this.isValidEmail(this.state.email);
72 | const isValidPassword = this.state.password.length > 1;
73 |
74 | return (
75 |
124 | );
125 | }
126 |
127 | render() {
128 | switch (this.state.stage) {
129 | case 0:
130 | default:
131 | return this.renderSignUp();
132 | case 1:
133 | return this.renderConfirm();
134 | }
135 | }
136 | }
137 |
138 |
139 | const withRouter = Component => props => {
140 | const location = useLocation();
141 | const navigate = useNavigate();
142 | const params = useParams();
143 |
144 | return (
145 |
151 | );
152 | };
153 |
154 | export default withRouter(SignUp);
155 |
--------------------------------------------------------------------------------
/resources/code/website/src/auth/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import SignIn from './SignIn';
16 | import SignUp from './SignUp';
17 |
18 | export {
19 | SignIn,
20 | SignUp
21 | };
22 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/BaseMap.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import { loadModules } from 'esri-loader';
17 |
18 | class BaseMap extends React.Component {
19 | static defaultProps = {
20 | mapOptions: {
21 | basemap: 'gray-vector'
22 | },
23 | viewOptions: {
24 | zoom: 12,
25 | center: [ -122.31, 47.60 ]
26 | }
27 | };
28 |
29 | constructor(props) {
30 | super(props);
31 | this.state = { status: 'loading' };
32 | this.esriOptions = {
33 | url: 'https://js.arcgis.com/4.6/'
34 | };
35 | this.style = {
36 | container: {
37 | height: '100vh',
38 | width: '100vw'
39 | },
40 | map: {
41 | padding: 0,
42 | margin: 0,
43 | height: '100%',
44 | width: '100%'
45 | }
46 | };
47 | }
48 |
49 | componentDidMount() {
50 | loadModules([ 'esri/Map', 'esri/views/MapView' ], this.esriOptions)
51 | .then(([Map, MapView]) => {
52 | const map = new Map(this.props.mapOptions);
53 | const view = new MapView({
54 | container: 'esriMapView',
55 | map,
56 | ...this.props.viewOptions
57 | });
58 | view.then(() => {
59 | this.setState({ map, view, status: 'loaded' });
60 | });
61 | });
62 | }
63 |
64 | render() {
65 | return (
66 |
67 |
68 | {this.state.status === 'loading' && (
Loading...
)}
69 |
70 |
71 | );
72 | }
73 | }
74 |
75 | export default BaseMap;
76 |
77 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/DynamicImage.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 |
17 | const DynamicImage = (props) => {
18 | const altText = props.alt || "dynamic image";
19 | return (
20 |
21 | );
22 | };
23 |
24 | export default DynamicImage;
25 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/ESRIMap.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import { loadModules } from 'esri-loader';
17 |
18 | class ESRIMap extends React.Component {
19 | static defaultProps = {
20 | mapOptions: {
21 | basemap: 'gray-vector'
22 | }
23 | };
24 |
25 | constructor(props) {
26 | super(props);
27 | this.state = { status: 'loading' };
28 | this.esriOptions = {
29 | url: 'https://js.arcgis.com/4.6/'
30 | };
31 | this.style = {
32 | container: {
33 | height: '100vh',
34 | width: '100vw'
35 | },
36 | map: {
37 | padding: 0,
38 | margin: 0,
39 | height: '100%',
40 | width: '100%'
41 | }
42 | };
43 | }
44 |
45 | /**
46 | * Loads the ESRI modules and returns them as an object
47 | */
48 | async loadEsriModules() {
49 | const [
50 | Map,
51 | MapView,
52 | Graphic,
53 | Point,
54 | TextSymbol,
55 | PictureMarkerSymbol,
56 | webMercatorUtils
57 | ] = await loadModules([
58 | 'esri/Map',
59 | 'esri/views/MapView',
60 | 'esri/Graphic',
61 | 'esri/geometry/Point',
62 | 'esri/symbols/TextSymbol',
63 | 'esri/symbols/PictureMarkerSymbol',
64 | 'esri/geometry/support/webMercatorUtils'
65 | ], this.esriOptions);
66 |
67 | return {
68 | Map,
69 | MapView,
70 | Graphic,
71 | Point,
72 | TextSymbol,
73 | PictureMarkerSymbol,
74 | webMercatorUtils
75 | };
76 | }
77 |
78 | async componentDidMount() {
79 | try {
80 | const ESRI = await this.loadEsriModules();
81 | this.xyToLngLat = ESRI.webMercatorUtils.xyToLngLat;
82 |
83 | const map = ESRI.Map({ basemap: 'gray-vector' });
84 | const view = ESRI.MapView({
85 | center: [-122.31, 47.60],
86 | container: 'esriMapView',
87 | map: map,
88 | zoom: 12
89 | });
90 |
91 | const pinSymbol = new ESRI.TextSymbol({
92 | color: '#f50856',
93 | text: '\ue61d',
94 | font: { size: 20, family: 'CalciteWebCoreIcons' }
95 | });
96 |
97 | var unicornSymbol = new ESRI.PictureMarkerSymbol({
98 | url: 'https://s3.amazonaws.com/aws-mobile-hub-images/wild-rydes/unicorn-icon.png',
99 | width: '25px',
100 | height: '25px'
101 | });
102 |
103 | this.pinGraphic = null;
104 | if (this.props.pinLocation) {
105 | this.selectedPoint = this.props.pinLocation;
106 | this.pinGraphic = new ESRI.Graphic({
107 | symbol: this.state.pinSymbol,
108 | geometry: this.selectedPoint
109 | });
110 | view.graphics.add(this.pinGraphic);
111 | }
112 |
113 | // Watch for map re-centering
114 | view.watch('center', (position) => this.updateCenter(position));
115 |
116 | // Watch for map pinch-and-zoom actions
117 | view.watch('extent', (extent) => this.updateExtent(extent));
118 |
119 | // Watch for map click events
120 | view.on('click', (event) => {
121 | this.unsetLocation();
122 | this.selectedPoint = event.mapPoint;
123 | this.pinGraphic = new ESRI.Graphic({
124 | symbol: this.state.pinSymbol,
125 | geometry: this.selectedPoint
126 | });
127 | view.graphics.add(this.pinGraphic);
128 |
129 | if (this.props.onMapClick) {
130 | this.props.onMapClick(this.selectedPoint);
131 | }
132 | });
133 |
134 | view.then(() => {
135 | // Set the current map settings in the object
136 | // once it is rendered
137 | this.updateCenter(view.center);
138 | this.updateExtent(view.extent);
139 |
140 | // Store the status of the map
141 | this.setState({
142 | map,
143 | view,
144 | pinSymbol,
145 | unicornSymbol,
146 | status: 'loaded'
147 | });
148 | });
149 | } catch (err) {
150 | console.error(err);
151 | }
152 | }
153 |
154 | /**
155 | * Updates the position of the map by re-centering.
156 | *
157 | * @param {Point} position the new center of the map
158 | */
159 | updateCenter(position) {
160 | this.center = {
161 | latitude: position.latitude,
162 | longitude: position.longitude
163 | }
164 | }
165 |
166 | /**
167 | * Updates the extents of the map - used when zooming
168 | *
169 | * @param {Rectangle} extent
170 | */
171 | updateExtent(extent) {
172 | if (typeof this.xyToLngLat !== 'undefined') {
173 | var min = this.xyToLngLat(extent.xmin, extent.ymin);
174 | var max = this.xyToLngLat(extent.xmax, extent.ymax);
175 | this.extent = {
176 | minLng: min[0],
177 | minLat: min[1],
178 | maxLng: max[0],
179 | maxLat: max[1]
180 | };
181 | }
182 | }
183 |
184 | unsetLocation() {
185 | this.selectedPoint = null;
186 | if (this.pinGraphic !== null) {
187 | this.state.view.graphics.remove(this.pinGraphic);
188 | this.pinGraphic = null;
189 | }
190 | }
191 |
192 | render() {
193 | return (
194 |
195 |
196 | {this.state.status === 'loading' && (
Loading...
)}
197 |
198 |
199 | );
200 | }
201 | }
202 |
203 | export default ESRIMap;
204 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/EmailSignUp.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 |
17 | class EmailSignUp extends React.Component {
18 | constructor(props) {
19 | super(props);
20 | this.state = { email: '', emailsubmitted: false };
21 | }
22 |
23 | onEmailChanged(event) {
24 | this.setState({ email: event.target.value.toLowerCase() });
25 | }
26 |
27 | /* TODO: HANDLE FORM INPUT */
28 | onEmailSubmitted(event) {
29 | event.preventDefault();
30 |
31 | this.setState({ email: '', emailsubmitted: true });
32 | }
33 | /* END OF PINPOINT CHANGES */
34 |
35 | render() {
36 | if (this.state.emailsubmitted) {
37 | return (
Thank you for signing up
);
38 | }
39 | return (
40 |
41 |
Sign Up
42 |
Wild Rydes is coming sooon! Enter your email to enter the limited private beta
43 |
47 |
48 | );
49 | }
50 | }
51 |
52 | export default EmailSignUp;
53 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/LegalFooter.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 |
17 | const LegalFooter = () => {
18 | return (
19 |
25 | );
26 | };
27 |
28 | export default LegalFooter;
29 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/PageList.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import { Link } from 'react-router-dom';
17 |
18 | const PageList = () => {
19 | const pages = [
20 | { url: '/', title: 'Home' },
21 | { url: '/unicorns', title: 'Meet the Unicorns' },
22 | { url: '/investors', title: 'Investors & Board of Directors' },
23 | { url: '/faq', title: 'FAQ' },
24 | { url: '/profile', title: 'Profile' },
25 | { url: '/register', title: 'Apply' }
26 | ];
27 |
28 | return (
29 |
30 | {
31 | pages.map((v, i) => (
32 |
{v.title}
33 | ))
34 | }
35 |
36 | );
37 | };
38 |
39 | export default PageList;
40 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/SiteFooter.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import LegalFooter from './LegalFooter';
17 | import PageList from './PageList';
18 |
19 | const SiteFooter = () => {
20 | return (
21 |
29 | );
30 | };
31 |
32 | export default SiteFooter;
33 |
--------------------------------------------------------------------------------
/resources/code/website/src/components/SiteNav.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import PageList from './PageList';
17 |
18 | class SiteNav extends React.Component {
19 | constructor(props) {
20 | super(props);
21 | this.state = {
22 | menuOpened: false
23 | };
24 | }
25 |
26 | onClick(event) {
27 | this.setState({ menuOpened: !this.state.menuOpened });
28 | }
29 |
30 | render() {
31 | const cl = this.state.menuOpened ? 'menu-opened' : 'menu-closed';
32 | const clickHandler = (event) => this.onClick(event);
33 |
34 | return (
35 |
36 |
39 |
42 |
43 | );
44 | }
45 | }
46 |
47 | export default SiteNav;
48 |
--------------------------------------------------------------------------------
/resources/code/website/src/css/ride.css:
--------------------------------------------------------------------------------
1 | .info {
2 | position: absolute;
3 | right: 10px;
4 | top: 10px;
5 | z-index: 100;
6 | width: 275px;
7 | background: transparent;
8 | }
9 |
10 | .info .panel-body {
11 | background: transparent;
12 | }
13 |
14 | .panel {
15 | background-color: white;
16 | border: 1px solid rgba(0,0,0,0);
17 | border-radius: 4px;
18 | box-shadow: 0 1px 1px rgba(0,0,0,0.05);
19 | }
20 |
21 | .panel-default {
22 | border-color: #dddddd;
23 | }
24 |
25 | .panel-default > .panel-heading {
26 | background-color: #f5f5f5;
27 | border-color: #dddddd;
28 | color: #333333;
29 | }
30 |
31 | .panel-heading {
32 | border-bottom: 1px solid rgba(0, 0, 0, 0);
33 | border-top-left-radius: 3px;
34 | border-top-right-radius: 3px;
35 | padding: 10px 15px;
36 | }
37 |
38 | .panel-title {
39 | color: inherit;
40 | font-size: 16px;
41 | margin-bottom: 0;
42 | margin-top: 0;
43 | }
44 |
45 | .configMessage {
46 | width: 100%;
47 | height: 100%;
48 | text-align: center;
49 | background: transparent;
50 | position: absolute;
51 | z-index: 2000;
52 | }
53 |
54 | .configMessage .panel {
55 | margin: auto;
56 | width: 40%;
57 | top: 50%;
58 | position: relative;
59 | transform: translateY(-50%);
60 | -ms-transform: translateY(-50%);
61 | -moz-transform: translateY(-50%);
62 | -webkit-transform: translateY(-50%);
63 | -o-transform: translateY(-50%);
64 | z-index: 100;
65 | }
66 |
67 | .configMessage .panel-body {
68 | text-align: left;
69 | }
70 |
71 | .configMessage .backdrop {
72 | position: fixed;
73 | top: 0;
74 | left: 0;
75 | width: 100%;
76 | height: 100%;
77 | background: #ffffff;
78 | opacity: 0.6;
79 | z-index: 50;
80 | }
81 |
82 | .idToken {
83 | word-wrap: break-word;
84 | font-family: sans-serif;
85 | font-size: 8px;
86 | }
87 |
88 | #request {
89 | position: relative;
90 | background: #fcc1d4 none repeat scroll 0 0;
91 | color: #000;
92 | border-color: #000;
93 | border-radius: 5px;
94 | text-align: center;
95 | width: 135px;
96 | }
97 |
98 | #request:disabled {
99 | color: #999;
100 | border-color: #999;
101 | }
102 |
103 | #updates {
104 | list-style: none;
105 | margin-top: 5px;
106 | padding: 0;
107 | }
108 |
109 | #updates li {
110 | margin: 3px 0;
111 | border: 1px solid #ccc;
112 | border-radius: 5px;
113 | background-color: #f7f7f7;
114 | padding: 5px;
115 | }
116 |
117 | #main, #map {
118 | position: absolute;
119 | top: 0;
120 | left: 0;
121 | width: 100%;
122 | height: 100%;
123 | padding: 0;
124 | margin: 0;
125 | }
126 |
--------------------------------------------------------------------------------
/resources/code/website/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import ReactDOM from 'react-dom/client';
17 | import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
18 | import { Home, FAQ, Investors, MainApp, Unicorns, Profile } from './pages';
19 | import { SignIn, SignUp } from './auth';
20 | import 'normalize.css';
21 |
22 | const isAuthenticated = () => false;
23 |
24 | const PrivateRoute = ({ component: Component, ...rest }) => (
25 | (
28 | isAuthenticated() === true
29 | ?
30 | :
31 | )} />
32 | );
33 |
34 | class App extends React.Component {
35 | render() {
36 | return (
37 |
38 |
39 | } />
40 | } />
41 | } />
42 | } />
43 | } />
44 | } />
45 | } />
46 | }/>
47 |
48 |
49 | );
50 | }
51 | }
52 |
53 | const root = ReactDOM.createRoot(document.getElementById('root'));
54 | root.render(
55 |
56 |
57 |
58 | );
59 |
--------------------------------------------------------------------------------
/resources/code/website/src/pages/FAQ.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import SiteNav from '../components/SiteNav';
17 | import SiteFooter from '../components/SiteFooter';
18 |
19 | import '../css/main.css';
20 |
21 | const FAQ = () => {
22 | return (
23 |
24 |
25 |
Wild Rydes
26 |
27 |
Frequently Asked Questions
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
Q: Why should I use this app?
36 |
A: Unicorns are faster, safer, and more reliable. In recent times, their numbers have grown significantly, reaching a scale that makes it finally possible to harness them for mass transportation at an affordable cost.
37 |
Q: How do you recruit the unicorns? How can I know that my unicorn is trustworthy?
38 |
A: Our unicorns are recruited from only the most humane and highest standard unicorn farms. Our unicorns are grass-fed, free range creatures raised on vegan, non-GMO diets. These unicorns are also completely safe because unicorns have infallible morality and judgment.
39 |
Q: How do I request a unicorn?
40 |
A: Simply download our app, then tap a button to begin. Your unicorn will arrive shortly.
41 |
Q: How much does it cost?
42 |
A: Since Wild Rydes is a marketplace for flight-based transportation, the price you pay is based on factors such as distance and availability of unicorns. You set the maximum price you’re willing to pay for a given ryde and then Wild Rydes matches you with a unicorn that’s willing to accept your price.
43 |
Q: How does it work?
44 |
A: Our product is powered by a complex algorithm which efficiently matches idle unicorns with ryders based on factors such as proximity and shortest time-to-destination. The system is built on a serverless architecture, which makes running and scaling our backend services simple and cost-effective, allowing us to reliably serve the needs of Wild Rydes’ ever growing user base.
45 |
Q: What if I have a complaint about my unicorn?
46 |
A: Wild Rydes is a customer obsessed company. We value each customer and want to ensure a positive experience. Therefore, we’ve staffed our customer service team with serverless chatbots that are available 24/7 to assist you.
47 |
Q: How do I cancel my ride?
48 |
A: Tap the “Cancel Ryde” button in the Wild Rydes app.
49 |
Q: Can I use Wild Rydes internationally?
50 |
A: Yes, you can use Wild Rydes in most countries except for Antarctica, Cuba, Sudan, Iran, North Korea, Syria and any other country designated by the United States Treasury's Office of Foreign Assets Control.
51 |
Q: How do I pay for my ryde?
52 |
A: After creating a Wild Rydes account, fill in your payment method such as credit card, debit card, Bitcoin wallet, or Vespene gas repository. After you complete your Ryde, you will automatically be charged the fare.
53 |
Q: How many passengers can my unicorn take?
54 |
A: The number of passengers on a single ryde depends on the size of your unicorn. Most unicorns can take one passenger per ryde. You can also request a large size unicorn which can take up to two passengers. If you select Sleigh version, you can take up to 4 passengers.
55 |
Q: What if I lose an item during my ryde?
56 |
A: Unfortunately, it’s unlikely we can retrieve your lost item if it has fallen off the unicorn during your ryde.
57 |
Q: How do I share my route information with someone else?
58 |
A: During your ryde, you can share your route and ETA with someone else using the Wild Rydes app. Simply tap the “Share Route” button and select a contact. Soon, they’ll be able to watch the status of your ryde.
59 |
Q: How do I rate my unicorn?
60 |
A: After your ryde completes, you have the option to rate your unicorn on the app. Our unicorns are customer obsessed and strive for 5 star ratings. Your feedback helps us improve our service!
61 |
Q: What if my unicorn doesn’t match the photo in the app?
62 |
A: The unicorn photo in your app should match the unicorn that arrives to pick you up. If they do not match, then Wild Rydes recommends that you do not board the unicorn. You should then immediately report the imposter unicorn to Wild Rydes.
63 |
Q: Can I use Concur with Wild Rydes?
64 |
A: Yes, you can connect your Concur profile to the Wild Rydes app so you can track business trips made on Wild Rydes.
65 |
Q: Can I request a specific unicorn?
66 |
A: While we do not allow requesting specific unicorns, you can choose the type and size of unicorn using the app.
67 |
Q: Why do you charge a service fee?
68 |
A: The service fee is a fixed charge added to every ryde. This helps us pay for our on-going maintenance and operating costs required to run the service and tend to our unicorn herd.
69 |
70 |
71 |
72 |
73 |
74 | );
75 | };
76 |
77 | export default FAQ;
78 |
79 |
--------------------------------------------------------------------------------
/resources/code/website/src/pages/Investors.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import SiteNav from '../components/SiteNav';
17 | import SiteFooter from '../components/SiteFooter';
18 |
19 | import '../css/main.css';
20 |
21 | const Investor = (props) => {
22 | const cl = `title ${props.className}`;
23 | return (
24 |
25 |
26 | {props.title}
27 |
28 |
{props.type}
29 |
{props.description}
30 |
31 | );
32 | };
33 |
34 | const InvestorsList = () => {
35 | const investors = [
36 | {
37 | className: 'pcp',
38 | title: 'Penglai Communications and Post New Century Technology Corporation Ltd',
39 | type: 'Global Communications Provider',
40 | description: 'PCPNCTC was formed in 2008 to hold the telecommunications services, media, and IT businesses of Penglai Communications and Post LTD, a multinational mass media and telecommunications company. PCPL provides broadband subscription television services, fixed telephone, and mobile telephone across 20 countries and 3 continents.'
41 | },
42 | {
43 | className: 'awesome',
44 | title: 'Tenderloin Capital',
45 | type: 'Venture Capital Firm',
46 | description: 'What makes us awesome sauce and not your typical venture firm? Backed by over three decades of experience and partnering successfully with entrepreneurs, Tenderloin Capital was founded to serve the needs of early-stage founders. It’s not just our experience that sets us apart; we relate to our entrepreneurs as people, not just as investments. Tenderloin Capital backs entrepreneurs who are building market-disrupting social-mobile-local-machine learned-artificially-intelligent cognitive experiences.'
47 | },
48 | {
49 | className: 'barn',
50 | title: 'The Barn',
51 | type: 'Accelerator',
52 | description: 'The Barn is an institution for primarily incubating chicken eggs as well as the next revolutions in precision agriculture technology. The Barn created the industry defining model for funding sustainable, humane, non-GMO, and fairtrade early stage businesses in animal husbandry. We look forward to working with you.'
53 | }
54 | ];
55 |
56 | return (
57 |
76 | Wild Rydes has a talented Board of Directors which advises the company on strategy and enabling business success. Using its collective leadership intangibles, the Board works with Wild Rydes to ideate solutions and form audacious ideas to the company’s most pressing business challenges. The Board and the Company work together to make informed problem solving decisions using process optimization and agile decision making techniques.
77 |
78 |
79 |
80 |
81 |
82 |
Dr. Tim Wagner
83 |
Chairman of the Board, Grand Master of the Serverless Rite
84 |
85 |
86 |
87 |
88 |
Vaughn R. Nicholson
89 |
EIR at Awesome Sauce Capital
90 |
91 |
92 |
93 |
94 |
95 |
96 |
Conway Bulle
97 |
Partner at The Barn
98 |
99 |
100 |
101 |
102 |
Dr. Samantha Walleford, PhD
103 |
Managing Partner at Tenderloin Capital
104 |
105 |
106 |
107 |
108 |
Qilin Fei
109 |
Chairman of the Central Committee for Planning at PENGLAI COMMUNICATIONS AND POST NEW CENTURY TECHNOLOGY CORPORATION LTD
We would not be anywhere without our trusted investors. We thank each of them for where we are today.
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | );
133 |
134 | export default Investors;
135 |
--------------------------------------------------------------------------------
/resources/code/website/src/pages/MainApp.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import BaseMap from '../components/BaseMap';
17 | import ESRIMap from '../components/ESRIMap';
18 | import Amplify from 'aws-amplify';
19 | import { Auth, API } from 'aws-amplify';
20 | import awsConfig from '../amplify-config';
21 | import '../css/ride.css';
22 |
23 | const apiName = 'WildRydesAPI';
24 | const apiPath = '/ride';
25 |
26 | class MainApp extends React.Component {
27 | constructor(props) {
28 | super(props);
29 | this.state = {
30 | authToken: null,
31 | idToken: null,
32 | requestRideEnabled: false,
33 | updates: [
34 | 'Welcome! Click the map to set your pickup location.'
35 | ]
36 | };
37 | }
38 |
39 | async componentDidMount() {
40 | const session = await Auth.currentSession();
41 | this.setState({ authToken: session.accessToken.jwtToken });
42 | this.setState({ idToken: session.idToken.jwtToken });
43 | }
44 |
45 | /**
46 | * Determines if the API is enabled
47 | *
48 | * @return {Boolean} true if API is configured
49 | */
50 | hasApi() {
51 | // const api = awsConfig.API.endpoints.filter(v => v.endpoint !== '');
52 | // return (typeof api !== 'undefined');
53 | }
54 |
55 | /**
56 | * Calls the backend API to retrieve the Unicorn data
57 | *
58 | * @param {Number} latitude
59 | * @param {Number} longitude
60 | */
61 | async getData(pin) {
62 | console.error('Request a Ride is not implemented');
63 | }
64 |
65 | /**
66 | * Called when Request Ride is called
67 | */
68 | async onClick() {
69 | if (!this.state.pin) {
70 | console.error('No pin present - skipping');
71 | return true;
72 | }
73 |
74 | const updates = [ 'Requesting Unicorn' ];
75 | try {
76 | this.setState({
77 | requestRideEnabled: false,
78 | updates
79 | });
80 | const data = await this.getData(this.state.pin);
81 | console.log(data);
82 | updates.push([ `Your unicorn, ${data.Unicorn.Name} will be with you in ${data.Eta}` ]);
83 | this.setState({ updates });
84 |
85 | // Let's fake the arrival
86 | setTimeout(() => {
87 | console.log('Ride Complete');
88 | const updateList = this.state.updates;
89 | updateList.push([ `${data.Unicorn.Name} has arrived` ]);
90 | this.setState({
91 | updates: updateList,
92 | requestRideEnabled: false,
93 | pin: null
94 | });
95 | }, data.Eta * 1000);
96 | } catch (err) {
97 | console.error(err);
98 | updates.push([ 'Error finding unicorn' ]);
99 | this.setState({ updates });
100 | }
101 | }
102 |
103 | /**
104 | * Called when the mapClick happens
105 | * @param {Point} position the position of the map pin
106 | */
107 | onMapClick(position) {
108 | console.log(`onMapClick(${JSON.stringify(position)})`);
109 | this.setState({ pin: position, requestRideEnabled: true });
110 | }
111 |
112 | render() {
113 | const hasApi = this.hasApi();
114 |
115 | // If API is not configured, but auth is, then output the
116 | // token.
117 | if (!hasApi) {
118 | return (
119 |
120 |
121 |
122 |
123 |
124 |
125 |
Successfully Authenticated!
126 |
127 |
128 |
This page is not functional yet because there is no API configured.
129 |
Here is your user's identity token:
130 |
{this.state.idToken}
131 |
132 |
133 |
134 |
135 | );
136 | }
137 |
138 | // If the API is configured, then display the "requestUnicorn"
139 | // button. If data is available (i.e. unicorn is requested),
140 | // then display the additional patterns (unicorn on map).
141 | const updateList = this.state.updates.map(
142 | (v, i) =>
{v}
143 | );
144 | return (
145 |
146 |
147 |
148 |
149 |
150 |
151 | {updateList}
152 |
153 |
154 |
155 | { this.onMapClick(position); }}/>
156 |
157 |
158 | );
159 | }
160 | }
161 |
162 | export default MainApp;
163 |
--------------------------------------------------------------------------------
/resources/code/website/src/pages/Profile.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import SiteNav from '../components/SiteNav';
17 | import SiteFooter from '../components/SiteFooter';
18 | import { Auth } from 'aws-amplify';
19 | import { S3Image } from '@aws-amplify/ui-react';
20 | import '../css/main.css';
21 |
22 | class Profile extends React.Component {
23 | constructor(props) {
24 | super(props);
25 | this.state = {
26 | user: {
27 | attributes: {
28 | email: 'me@example.com',
29 | phone_number: '+1123456789'
30 | }
31 | }
32 | }
33 | }
34 | componentDidMount() {
35 | Auth.currentAuthenticatedUser().then(user => {
36 | console.log('Cognito User', user);
37 | this.setState({user, image_key: 'profile-' + user.attributes.sub + '.jpg'});
38 | });;
39 | }
40 |
41 | async onImageLoad(url) {
42 | console.error('onImageLoad is not yet implemented');
43 | }
44 |
45 | render() {
46 | return (
47 |
48 |
49 | {/* this.onImageLoad(url)} picker/> */}
50 |
51 |
52 |
53 |
E-mail:
54 |
{this.state.user.attributes.email}
55 |
56 |
57 |
Phone:
58 |
{this.state.user.attributes.phone_number}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | );
68 | }
69 | }
70 |
71 | export default Profile;
72 |
--------------------------------------------------------------------------------
/resources/code/website/src/pages/Unicorns.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import React from 'react';
16 | import DynamicImage from '../components/DynamicImage';
17 | import SiteNav from '../components/SiteNav';
18 | import SiteFooter from '../components/SiteFooter';
19 |
20 | import '../css/main.css';
21 |
22 | const Unicorns = () => (
23 |
24 |
25 |
Wild Rydes
26 |
27 |
Unicorns Are Our Friends
28 |
29 | The app is what makes this service exist, but the unicorns make it move. Meet them and see who you are riding with!
30 |
31 |
32 |
33 |
34 |
35 |
36 | Wild Rydes has a dedicated staff that recruits, trains, and tends to our herd of unicorns. We take great pride in the quality of unicorns and rydes that we provide to our customers, and our staff exercises the utmost care in vetting the unicorns that join our herd.
37 |
38 |
39 | Every unicorn goes through a rigorous due diligence process where we perform background checks, flying exams, and several rounds of interviews. Unicorns accepted to Wild Rydes are then treated to the best care and maintenance possible. We provide them excellent benefits, health care, and employee perks. This is part of our company philosophy in which happy unicorns lead to happy customers.
40 |
41 |
Meet a few of the unicorns that are part of our family.
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Bucephalus
51 |
Golden Swiss
52 |
53 | Bucephalus joined Wild Rydes in February 2016 and has been giving rydes almost daily. He says he most enjoys getting to know each of his ryders, which makes the job more interesting for him. In his spare time, Bucephalus enjoys watching sunsets and playing Pokemon Go.
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
Shadowfox
66 |
Brown Jersey
67 |
68 | Shadowfox joined Wild Rydes after completing a distinguished career in the military, where he toured the world in many critical missions. Shadowfox enjoys impressing his ryders with magic tricks that he learned from his previous owner.
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
Rocinante
81 |
Baby Flying Yellowback
82 |
83 | Rocinante recently joined the Wild Rydes team in Madrid, Spain. She was instrumental in forming Wild Rydes’ Spanish operations after a long, distinguished acting career in windmill shadow-jousting.
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | );
93 |
94 | export default Unicorns;
95 |
--------------------------------------------------------------------------------
/resources/code/website/src/pages/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | import Home from './Home';
16 | import FAQ from './FAQ';
17 | import Investors from './Investors';
18 | import MainApp from './MainApp';
19 | import Unicorns from './Unicorns';
20 | import Profile from './Profile';
21 |
22 | export {
23 | Home,
24 | FAQ,
25 | Investors,
26 | MainApp,
27 | Unicorns,
28 | Profile,
29 | };
30 |
--------------------------------------------------------------------------------
/resources/policies/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-serverless-auth-workshop/335a2600336c09eca95375d3adbf8ea1bb0131ad/resources/policies/.gitkeep
--------------------------------------------------------------------------------
/resources/templates/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-serverless-auth-workshop/335a2600336c09eca95375d3adbf8ea1bb0131ad/resources/templates/.gitkeep
--------------------------------------------------------------------------------
/workshop/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-serverless-auth-workshop/335a2600336c09eca95375d3adbf8ea1bb0131ad/workshop/.DS_Store
--------------------------------------------------------------------------------
/workshop/buildspec.yml:
--------------------------------------------------------------------------------
1 | version: 0.2
2 | phases:
3 | install:
4 | runtime-versions:
5 | golang: 1.12
6 | nodejs: 10
7 | commands:
8 | - echo Entered the install phase...
9 | - apt-get -qq update && apt-get -qq install curl
10 | - apt-get -qq install asciidoctor
11 | - curl -s -L https://github.com/gohugoio/hugo/releases/download/v0.55.6/hugo_0.55.6_Linux-64bit.deb -o hugo.deb
12 | - dpkg -i hugo.deb
13 | finally:
14 | - echo Installation done
15 | build:
16 | commands:
17 | - echo Entered the build phase ...
18 | - echo Build started on `date`
19 | - cd $CODEBUILD_SRC_DIR/workshop/themes
20 | - git clone https://github.com/matcornic/hugo-theme-learn.git learn
21 | - cd ../
22 | - hugo --quiet
23 | finally:
24 | - echo Building the HTML files finished
25 | artifacts:
26 | files:
27 | - "**/*"
28 | base-directory: $CODEBUILD_SRC_DIR/workshop/public/
29 | discard-paths: no
30 |
--------------------------------------------------------------------------------
/workshop/config.toml:
--------------------------------------------------------------------------------
1 | RelativeURLs=true
2 | CanonifyURLs=true
3 | languageCode = "en-US"
4 | defaultContentLanguage = "en"
5 |
6 | title = "Serverless Auth "
7 | theme = "learn"
8 | metaDataFormat = "yaml"
9 | defaultContentLanguageInSubdir= true
10 |
11 | uglyurls = true
12 | sectionPagesMenu = "main"
13 | pygmentsCodeFences = true
14 | pygmentsStyle = "monokai"
15 |
16 | [params]
17 | editURL = "https://github.com/aws-samples/aws-serverless-auth-workshop"
18 | description = "Serverless Identity Management, Authentication, and Authorization Workshop"
19 | author = "Chris McPeek "
20 | disableBreadcrumb = false
21 | disableNextPrev = false
22 | themeVariant = "aws"
23 | disableSearch = false
24 | disableAssetsBusting = true
25 | disableLanguageSwitchingButton = false
26 | disableShortcutsTitle = true
27 | disableInlineCopyToClipBoard = true
28 | artifactUrlPrefix = "https://serverless-workshops-artifacts.s3.amazonaws.com/wildrydes-auth/dev"
29 |
30 | [outputs]
31 | home = [ "HTML", "RSS", "JSON"]
32 |
33 | [blackfriday]
34 | plainIDAnchors = true
35 | hrefTargetBlank = true
36 |
37 | [Languages]
38 | [Languages.en]
39 | title = ""
40 | weight = 1
41 | languageName = "English"
42 |
--------------------------------------------------------------------------------
/workshop/content/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Serverless Identity Management, AuthN, and AuthZ Workshop"
3 | chapter = true
4 | weight = 1
5 | +++
6 | In this workshop, you will build a serverless microservices application that enables users to request unicorn rides from the Wild Rydes fleet. The application will present users with a user interface for signing up, signing in, indicating their location to request a ride, and managing their rider profile.
7 |
8 | This application architecture demonstrates end-to-end authentication and authorization patterns through the use of [Amazon Cognito](https://aws.amazon.com/cognito/), [Amazon API Gateway](https://aws.amazon.com/api-gateway/), [AWS Lambda](https://aws.amazon.com/lambda/), and [AWS Identity and Access Management (IAM)](https://aws.amazon.com/iam/). A single page [React JS](https://reactjs.org/) web app hosts the HTML, CSS and Javascript to render the front-end which then connects to a public serverless backend API built using Amazon API Gateway and AWS Lambda. Amazon Cognito provides user identity management and authentication functions to secure the backend API. Finally, DynamoDB provides a persistence layer where data is stored and retrieved via the API's Lambda function.
9 |
10 | 
11 |
12 |
13 | ### Issues, Comments, Feedback?
14 |
15 | I’m open source! If you see an issue, want to contribute content, or have overall feedback please open an issue or pull request in our GitHub repository: [github.com/aws-samples/aws-serverless-auth-workshop/](https://github.com/aws-samples/aws-serverless-auth-workshop/).
16 |
17 | {{% button href="https://github.com/aws-samples/aws-serverless-auth-workshop/issues" icon="fas fa-bug" %}}Report an issue{{% /button %}}
18 | {{% button href="https://aws.amazon.com/serverless/developer-tools" icon="fas fa-graduation-cap" %}}Learn more{{% /button %}}
19 |
--------------------------------------------------------------------------------
/workshop/content/cleanup/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Cleanup Resources"
3 | chapter = true
4 | weight = 60
5 | pre = "4. "
6 | +++
7 |
8 | To prevent your account from accruing additional charges, you should remove any resources that are no longer needed.
--------------------------------------------------------------------------------
/workshop/content/cleanup/backend/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Remove WildRydes Backend"
3 | weight = 64
4 | +++
5 |
6 | Next, you will need to remove the ***CloudFormation*** stack for the API. This stack should be named **WildRydesBackend**. Once again, from the your terminal window, run:
7 |
8 | ```bash
9 | aws cloudformation delete-stack --stack-name WildRydesBackend
10 | ```
11 |
12 | > If you changed the name of your stack from the default, you will need to update the stack name to what you changed it to. If you clicked the quick link in the instructions, no adjustment to the above command is needed. You can run `aws cloudformation describe-stacks` to find the your stack name.
--------------------------------------------------------------------------------
/workshop/content/cleanup/cloud9/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Remove Cloud9 and VPC Stack"
3 | weight = 65
4 | +++
5 |
6 | Lastly, you will need to remove the ***CloudFormation Stack*** for the **Cloud9 instance** and its VPC. This stack should be named **WildRydes-Cloud9**. Deleting this stack will **shut down and permanently delete your Cloud9 environment** and all code or projects within so be sure you want to proceed before executing this command.
7 |
8 | ```bash
9 | aws cloudformation delete-stack --stack-name WildRydes-Cloud9
10 | ```
11 |
12 | > If you changed the name of your stack from the default, you will need to update the stack name to what you changed it to. If you clicked the quick link in the instructions, no adjustment to the command above is needed. You can run `aws cloudformation describe-stacks` to find your stack name.
--------------------------------------------------------------------------------
/workshop/content/cleanup/cognito/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Remove Cognito Resources"
3 | weight = 62
4 | +++
5 |
6 | 1. From your **Cloud9 developer environment** run the following:
7 |
8 | ```bash
9 | aws cognito-identity delete-identity-pool --identity-pool-id YOUR-IDENTITY-POOL-ID-HERE
10 | ```
11 |
12 | > Copy and paste your Cognito identity pool ID from your scratch pad (example: us-west-2:b4b755cd-d359-42a1-9b49-f0e73f5b2571).
13 |
14 | > If you closed your scratch pad with your Cognito idenity pool ID, you can run the following list call via CLI to find the proper identiy pool ID, then run the delete call above.
15 |
16 | ```bash
17 | aws cognito-identity list-identity-pools --max-results 10
18 | ```
19 |
20 | 1. Next, run the following command to delete the Cognito User Pool you created:
21 |
22 | ```bash
23 | aws cognito-idp delete-user-pool --user-pool-id YOUR-USER-POOL-ID-HERE
24 | ```
25 |
26 | > Copy and paste your user pool ID from your scratch pad (example: us-west-2:us-west-2_srLwFQiEC)
27 |
28 | > If you closed your scratch pad with your user pool ID, you can run the following list call via CLI to find the proper user pool id, then run the delete call above.
29 |
30 | ```bash
31 | aws cognito-idp list-user-pools --max-results 10
32 | ```
33 |
--------------------------------------------------------------------------------
/workshop/content/cleanup/iam/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Detach IAM Policy"
3 | weight = 63
4 | +++
5 |
6 | #### Detach CognitoIdentityPoolAuthStandardPolicy IAM Policy
7 |
8 | 1. Before you delete the backend stack, you will need to remove the IAM Policy that you manually attached to the Auth role.
9 |
10 | Navigate to the Identity and Access Management (IAM) Console and search for the Auth role and click into it.
11 |
12 | 
13 |
14 | 1. On the Role Summary page, find the policy named **WildRydesAPI-StandardUserPolicy** in the Permissions tab. Once you locate the policy, click the **X** to remove this policy from the IAM Role. A popup window will ask you to confirm that you want to remove it - click the red **Detach** button.
--------------------------------------------------------------------------------
/workshop/content/cleanup/s3/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Empty and remove S3 Bucket"
3 | weight = 61
4 | +++
5 |
6 | 1. First, you need to empty the ***S3 bucket*** that was created by the Serverless Backend CloudFormation template.
7 |
8 | 1. Go the AWS Management Console, click **Services** then select **CloudFormation** under Management Tools.
9 |
10 | 1. In the **CloudFormation** console, click on your ***Wild Rydes*** stack name, such as `WildRydesBackend`.
11 |
12 | 1. Click on the **Outputs** tab.
13 |
14 | 1. Copy your bucket name to your clipboard. It is the name shown under Value for the key called `WildRydesProfilePicturesBucket`.
15 |
16 | 1. Open your Cloud9 developer environment.
17 |
18 | 1. Within the Cloud9 IDE, open up a terminal. You can do this by clicking the `+` icon in the lower pane and selecting **New Terminal**.
19 |
20 | 
21 |
22 | 1. Paste the following command and be sure to update your S3 bucket name:
23 |
24 | ```bash
25 | aws s3 rb s3://MY-BUCKET-NAME --force
26 | ```
--------------------------------------------------------------------------------
/workshop/content/iam_auth/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "AWS integration with IAM-based AuthZ"
3 | chapter = true
4 | weight = 50
5 | pre = "3. "
6 | +++
7 |
8 | In this module, you will expand your Wild Rydes application by enabling a profile management and profile photo management capabilities. [Amazon Cognito](https://aws.amazon.com/cognito/) will be used to store your user's profile information and custom attributes whereas [Amazon S3](https://aws.amazon.com/s3/) will store your user's profile pictures, with a link to the photo only being stored in the user's profile directly.
--------------------------------------------------------------------------------
/workshop/content/iam_auth/architecture/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Solution Architecture"
3 | weight = 51
4 | +++
5 |
6 | Building on Modules 1 and 2, this module will add photo storage and management via an Amazon S3 bucket. For AWS resource access from a web application, Amazon Cognito will issue not only JWTs as we saw earlier, but then also allow users to assume an IAM role from within the application. This AWS IAM role will then allow their application to securely connect to upload and download photos from S3 (though any other AWS API would also work with this capability). To secure access to the photo storage and bucket, you will leverage IAM policies for fine-grained control.
7 |
8 | 
9 |
10 | ---
11 | #### Implementation Overview
12 |
13 | Each of the following sections provides an implementation overview and detailed, step-by-step instructions. The overview should provide enough context for you to complete the implementation if you're already familiar with the AWS Management Console or you want to explore the services yourself without following a walkthrough.
14 |
15 | If you're using the latest version of the Chrome, Firefox, or Safari web browsers the step-by-step instructions won't be visible until you expand the section.
--------------------------------------------------------------------------------
/workshop/content/iam_auth/conclusion/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Conclusion"
3 | weight = 56
4 | +++
5 |
6 | Congratulations! You've completed the Wild Rydes Auth workshop. We hope that this time and interactive learning has been valuable for you. For further learning on this topic, please see our list of [Serverless Auth Resources](https://github.com/aws-samples/aws-serverless-workshops/blob/master/Auth/Resources.md).
7 |
8 | Please remember to run through the Clean up steps to ensure you decommission all resources spun up during the workshop today.
9 |
10 | Thank you for participating in this workshop!
--------------------------------------------------------------------------------
/workshop/content/iam_auth/configure/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Configure IAM permissions"
3 | weight = 53
4 | +++
5 |
6 | Though you could now attempt uploading photos via AWS Amplify, Amplify would use your Cognito Identity Pool roles that were created in module 1 which currently has no policies associated so you would not have access to the S3 bucket created. You need to next update our roles to have policies that grant access to our S3 photo bucket.
7 |
8 | ### High-Level Instructions
9 |
10 | Browse to the IAM console and find your Cognito Identity Pool's authenticated user role. Create an in-line policy on this role which provides for [S3 bucket protected and private-level access](https://aws-amplify.github.io/docs/js/storage#file-access-levels) per-user by leveraging IAM policy variables.
11 |
12 | {{% expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. Go the AWS Management Console, click **Services** then select **IAM** under Security, Identity, and Compliance.
15 |
16 | 1. Choose **Roles**.
17 |
18 | 1. Search for ***WildRydes*** to find the two roles which were created by Cognito Identity Pools when you created the Identity Pool in module one. Should you not be able to find the roles here, you can alternatively go to the **Cognito Federated Identities** console, find the correct identity pool, then click **Edit Identity Pool** in the top-right corner to see the roles listed. Each identity pool has both an Unauthenticated user role and an Authenticated user role.
19 |
20 | 1. Once you have found the names of the roles, go back to the IAM console and select the Auth role for your authenticated users.
21 |
22 | > If the full name of the role is hidden from view due to column width, you can hover over the partially visible name of the role to see the full name of the role as a tool tip.
23 |
24 | 
25 |
26 | 1. We want to grant permissions to this role explicitly so we will use an inline policy, which would be deleted with this role if it were ever to be deleted.
27 |
28 | 1. Choose **Add inline policy** on the right-hand side to create a new inline policy associated to this IAM role.
29 |
30 | 
31 |
32 | 1. Choose the **JSON** tab to allow you to free-form edit the new policy.
33 |
34 | 1. Paste the following IAM policy statements for S3 access. After pasting, you will need to go **replace the bucket name** listed in all caps with your bucket name (a total of 4 times).
35 |
36 | > Be sure to leave the parts of the resource names before and after the replacement value alone and not accidentally modify them.
37 |
38 | > The following policy makes use of IAM policy variables where ***${aws:userid}*** represents the current authenticated user's unique Cognito identity ID. This policy's effective permissions will allow all authenticated users to read objects from the root of the bucket and any /protected path, but only allow users to read their own private sub-path and write to their sub-path within the protected path. These are default paths that are integrated with AWS Amplify to easily set [file access levels](https://aws-amplify.github.io/docs/js/storage#file-access-levels).
39 |
40 | ```
41 | {
42 | "Version": "2012-10-17",
43 | "Statement": [
44 | {
45 | "Effect": "Allow",
46 | "Action": [
47 | "s3:PutObject",
48 | "s3:GetObject",
49 | "s3:GetObjectVersion",
50 | "s3:DeleteObject",
51 | "s3:DeleteObjectVersion"
52 | ],
53 | "Resource": "arn:aws:s3:::REPLACE_WITH_YOUR_BUCKET_NAME/private/${aws:userid}/*"
54 | },
55 | {
56 | "Effect": "Allow",
57 | "Action": [
58 | "s3:GetObject",
59 | "s3:GetObjectVersion"
60 | ],
61 | "Resource": "arn:aws:s3:::REPLACE_WITH_YOUR_BUCKET_NAME/protected/*"
62 | },
63 | {
64 | "Effect": "Allow",
65 | "Action": [
66 | "s3:PutObject",
67 | "s3:DeleteObject",
68 | "s3:DeleteObjectVersion"
69 | ],
70 | "Resource": "arn:aws:s3:::REPLACE_WITH_YOUR_BUCKET_NAME/protected/${aws:userid}/*"
71 | },
72 | {
73 | "Effect": "Allow",
74 | "Action": [
75 | "s3:PutObject",
76 | "s3:GetObject",
77 | "s3:GetObjectVersion",
78 | "s3:DeleteObject",
79 | "s3:DeleteObjectVersion"
80 | ],
81 | "Resource": "arn:aws:s3:::REPLACE_WITH_YOUR_BUCKET_NAME/public/*"
82 | }
83 | ]
84 | }
85 | ```
86 |
87 | 1. Choose **Review** policy.
88 |
89 | 1. Name the policy `WildRydes-S3Access`.
90 |
91 | 1. After reviewing for accuracy and any syntax errors, choose **Create policy**.
92 |
--------------------------------------------------------------------------------
/workshop/content/iam_auth/s3/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Setup S3 Bucket"
3 | weight = 52
4 | +++
5 |
6 | You will need to configure AWS Amplify to securely store profile images in an S3 bucket. To save time, the Serverless Backend CloudFormation template that created the serverless backend API for this workshop also created an S3 bucket for this purpose with the cross-origin resource sharing (CORS) settings already set. You just need to associate this bucket with your application's code.
7 |
8 | ### High-Level Instructions
9 |
10 | Browse to your CloudFormation stack created in the earlier modules and find the name of the S3 bucket under Outputs. Once you have the name, open your ***amplify-config.js*** file again and update the storage section with the bucket's name and region.
11 |
12 | {{% expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. Go the AWS Management Console, click **Services** then select **CloudFormation** under Management Tools.
15 |
16 | 1. In the CloudFormation console, click on your Wild Rydes stack name, such as **WildRydesBackend**.
17 |
18 | 1. Click on the **Outputs** tab.
19 |
20 | 1. Copy your bucket name to your clipboard. It is the name shown under `Value` for the key called ***WildRydesProfilePicturesBucket***.
21 |
22 | 1. Next, return to your Cloud9 IDE and open the file **/website/src/amplify-config.js**.
23 |
24 | 1. Fill in values for both the bucket name, which you just copied, as well as the region where your CloudFormation template was launched
25 |
26 | 1. Your final structure for the storage configuration of **amplify-config.js** should look like the following.
27 |
28 | ```javascript
29 | Storage: {
30 | bucket: 'wildrydes-profilepicturesbucket-1rmvuic97osxd',
31 | region: 'us-east-1'
32 | }
33 | ```
34 |
--------------------------------------------------------------------------------
/workshop/content/iam_auth/store/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Store Profile Pics in Profile"
3 | weight = 55
4 | +++
5 |
6 | With our image uploads now working, all will work as expected until you close your browser, but at that point the reference between your user profile and your profile picture will be lost. To fix this, you will leverage a Cognito User Pools user attribute called ***picture*** to persist the S3 object key so the same image can be loaded upon each login and persisted to be shown to the unicorns when you request a ride. You will need to update ***/website/src/pages/Profile.js*** and a method called ***onImageLoad*** to make this possible.
7 |
8 | ### High-Level Instructions
9 |
10 | Implement a method to persist the images uploaded to the current user's Cognito ***picture*** attribute each time the image is changed.
11 |
12 | {{% expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. Open your Cloud9 IDE environment and open the file at ***/website/src/pages/Profile.js***.
15 |
16 | 1. The S3Image UI component has a built-in method called ***onImageLoad*** which provides in its invocation the full URL of any image uploaded. We will make use of this built-in function to persist our image URLs out to Cognito.
17 |
18 | 1. Replace the existing ***onImageLoad*** function with the following code:
19 |
20 | ```javascript
21 | async onImageLoad(url) {
22 | if (!this.state.user.getSession) { return };
23 | console.log('Profile Picture URL:', url);
24 | try {
25 | let result = await Auth.updateUserAttributes(this.state.user, {
26 | 'picture': this.state.image_key
27 | });
28 | console.log(result);
29 | } catch (ex) {
30 | console.error('Attribute update error:', ex);
31 | }
32 | }
33 | ```
34 | 1. Now with this new method in place, upload a new photo after logging into Wild Rydes then close your browser. Open a new window and try logging in again. Your photo should load as it did previously.
--------------------------------------------------------------------------------
/workshop/content/iam_auth/update/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Update application"
3 | weight = 54
4 | +++
5 |
6 | Now that your IAM policies and Amplify SDK are initialized, you will be able to upload photos and render S3 photos with minimal code using Amplify's built-in UI components. S3 image is the component used to both render image objects for a React application, as well as embeding an image picker to help with uploads.
7 |
8 | ### High-Level Instructions
9 |
10 | Authenticate in the Wild Rydes app if you're not already logged in, then browse to the ***/profile*** path. You will see that your Cognito User Pool attributes are being read dynamically by the system. Next, you will add an [image picker](https://aws-amplify.github.io/docs/js/storage#s3image) from AWS Amplify to render a UI component for uploading and displaying photos stored in S3. These profile photos will be used to personalize the rider experience so unicorns know who to look for when picking up passengers.
11 |
12 | {{% expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. After logging in to Wild Rydes (if you're not authenticated already), browse to the **/profile** path.
15 |
16 | 1. You should see that your e-mail address and phone number you registered with are displayed which are all of your currently populated attributes.
17 |
18 | 1. Open your Cloud9 IDE environment and open the file at ***/website/src/pages/Profile.js***.
19 |
20 | 1. **Uncomment** the line that says ***S3Image***. This instantiates an Amplify UI component for React apps for image rendering and uploading and only requires this single line of code.
21 |
22 | 1. Go back to the Wild Rydes app and visit the **/profile** path after logging in. You should now be able to upload photos with the new image picker.
--------------------------------------------------------------------------------
/workshop/content/optional/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Optional Fine-grained IAM Authorization"
3 | chapter = true
4 | weight = 40
5 | pre = "2a. "
6 | +++
7 |
8 | In this optional extension to module 2, you will update your serverless backend for your Wild Rydes application leveraging [Amazon API Gateway](https://aws.amazon.com/api-gateway/) and [AWS Lambda](https://aws.amazon.com/lambda/) to use request signing with IAM-based authorization as a more secure authentication option.
9 |
10 | If you would like to skip this optional extension, you are able to proceed to module 3 directly, IAM-based Authorization.
--------------------------------------------------------------------------------
/workshop/content/optional/architecture/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Solution Architecture"
3 | weight = 41
4 | +++
5 |
6 | Building on Module 2, this module updates our Serverless backend built earlier using Amazon API Gateway and AWS Lambda to use IAM-based authorization. This extends our authorization capability to offer fine-grained access control authorizing differently per API operation and enhancing security via request signing. By enabling IAM-based authorization, you will use the same type of authentication, authorization, and request signing used by all AWS services and SDKs.
7 |
8 | [Request signing](https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html) is a more secure implementation of API request authentication where each API request made is signed with a signature unique to the request itself. Hence, no static API keys or bearer tokens are directly sent to the backend service and any man-in-the-middle attacks would not be able to use such API keys or bearer tokens to impersonate a valid user with the backend resources. AWS APIs and SDKs use a request signing algorithm named [Signature V4 (Sigv4)](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) which is what you will enable your API to use in this module.
9 |
10 | > For production APIs, you should use either the token-based authorization OR request signing authorization via IAM demonstrated in this module, but not use both for the same API.
11 |
12 | 
13 |
14 | ---
15 | #### Implementation Overview
16 |
17 | Each of the following sections provides an implementation overview and detailed, step-by-step instructions. The overview should provide enough context for you to complete the implementation if you're already familiar with the AWS Management Console or you want to explore the services and documentation yourself without following a walkthrough.
18 |
19 | If you're using the latest version of the Chrome, Firefox, or Safari web browsers the step-by-step instructions won't be visible until you expand the section.
20 |
--------------------------------------------------------------------------------
/workshop/content/optional/associate/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Associate an API Gateway"
3 | weight = 42
4 | +++
5 |
6 | For us to be able to use request signing and IAM-based fine-grained access control, we'll first need to associate an IAM policy that provides permissions to invoke API operations for your API Gateway deployment. For further details, you can review [controlling access to an API with IAM permissions](https://docs.aws.amazon.com/apigateway/latest/developerguide/permissions.html) documentation.
7 |
8 | ### High-Level Instructions
9 |
10 | In the IAM console, associate the **WildRydesAPI-StandardUserPolicy** with your Cognito Identity Pool's authenticated user role to provide all authenticated users access to invoke operations the **/ride** path.
11 |
12 | {{% expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. Go the AWS Management Console, click **Services** then select **IAM** under Security, Identity, and Compliance.
15 |
16 | 1. Choose **Policies**.
17 |
18 | 1. Search for ***WildRydes*** to see the ***WildRydesAPI-StandardUserPolicy*** which was created by the Serverless Backed CloudFormation template.
19 |
20 | 
21 |
22 | 1. Click the **WildRydesAPI-StandardUserPolicy** policy name.
23 |
24 | 1. Review the policy which was created by CloudFormation to authorize requests to your API Gateway deployment.
25 |
26 | 
27 |
28 | > This policy allows access to invoke any method on the /ride path for any API stage of your API gateway backend. For more details about authoring IAM policies for API Gateway, visit the controlling access to an API with IAM permissions documentation.
29 |
30 | 1. Choose **Roles**.
31 |
32 | 1. Search for **WildRydes** to find the two roles which were created by Cognito Identity Pools when you created the Identity Pool in module one. Should you not be able to find the roles here, you can alternatively go to the **Cognito Federated Identities** console, find the correct identity pool, then click **Edit Identity Pool** in the top-right corner to see the roles listed. Each identity pool has both an Unauthenticated user role and an Authenticated user role.
33 |
34 | 1. Once you have found the names of the roles, go back to the IAM console and select the Auth role for your authenticated users.
35 |
36 | > If the full name of the role is hidden from view due to column width, you can hover over the partially visible name of the role to see the full name of the role as a tool tip.
37 |
38 | 
39 |
40 | 1. Choose **Attach policies**.
41 |
42 | 1. Search for `WildRydes` and check the box next to the policy named ***WildRydesAPI-StandardUserPolicy***.
43 |
44 | 
45 |
46 | 1. Choose **Attach policy**.
47 |
48 | 1. You should now see the ***WildRydesAPI-StandardUserPolicy*** policy associated with your Cognito IAM auth role.
49 |
50 | 
51 |
52 |
--------------------------------------------------------------------------------
/workshop/content/optional/enable_auth/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Enable API Gateway Authorization"
3 | weight = 43
4 | +++
5 |
6 | In addition to using JSON Web Tokens (JWTs) for authentication, API Gateway can leverage AWS request signing and parse the request signature to determine the requesting user. In this step, you'll update your authorization type to ***IAM*** for your API which will then use AWS's Identity and Access Management (IAM) capabilities to authorize requests via IAM policies.
7 |
8 | ### High-Level Instructions
9 |
10 | In the Amazon API Gateway console, update the authorization type to ***AWS_IAM*** for the ***POST*** method on the ***/ride*** resource. Next, re-deploy the API to make your change take effect.
11 |
12 | {{% expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. In the AWS Management Console choose **Services** then select **API Gateway** under Networking and Content Delivery.
15 |
16 | 1. Choose the API named ***WildRydes***.
17 |
18 | 1. Browse to **Resources** while within your Wild Rydes API in the API Gateway console.
19 |
20 | 1. Select the **POST** method under the ***/ride*** resource path.
21 |
22 | 1. Choose **Method Request**
23 |
24 | 
25 |
26 | 1. Choose the pencil icon next to `Authorization` to edit the setting.
27 |
28 | 1. Select **AWS_IAM** from the list of authorization options presented.
29 |
30 | 
31 |
32 | 1. **Save** your selection by clicking the checkmark icon next to the drop down.
33 |
34 | 
35 |
36 | 1. Next, choose the **Actions** button at the top of the resources list.
37 |
38 | 1. Choose **Deploy API** from the list of options presented.
39 |
40 | 1. For deployment stage, select `prod` then click **Deploy**.
41 |
42 | 1. You've now successfully deployed your new authentication integration to your API's production environment.
43 |
44 | ---
45 | #### Configure your Wild Rydes web app to authenticate API requests
46 |
47 | Now that you've deployed the new authorizer configuration to production, all API requests must be authenticated to be processed.
48 |
49 | 1. Return to your Wild Rydes app, sign in at ***/signin*** if necessary, and attempt to request a ride.
50 |
51 | 1. You should receive an ***Error finding unicorn***. If you open the developer console, you will see that we received a HTTP 401 error, which means it was an unauthorized request.
52 |
53 | > If at first your requests go through without any errors, try requesting a ride again in 30-60 seconds to allow time for the API Gateway changes to fully propagate.
54 |
55 | 1. Go back to Cloud9 and open the ***/website/src/pages/MainApp.js*** files.
56 |
57 | 1. Update your current ***getData*** method to the following method, which removes the ***Authorization*** header and adds debugging information to show us the request signature as requests are sent. The default behavior of the AWS Amplify library is the sign all requests with SigV4 signing when no authorization header is specified, so this will automatically sign all requests using this algorithm without extra development effort. **Save your changes** after making this update.
58 |
59 | ```javascript
60 | async getData(pin) {
61 | Amplify.Logger.LOG_LEVEL = 'DEBUG';
62 | const apiRequest = {
63 | body: {
64 | PickupLocation: {
65 | Longitude: pin.longitude,
66 | Latitude: pin.latitude
67 | }
68 | },
69 | headers: {
70 | 'Content-Type': 'application/json'
71 | }
72 | };
73 | console.log('API Request:', apiRequest);
74 | return await API.post(apiName, apiPath, apiRequest);
75 | }
76 | ```
77 |
78 | 1. Allow the application to refresh, sign-in again, and request a ride.
79 |
80 | 1. The unicorn ride request should be fulfilled as before now. To see the full request headers which were sent, look at the developer console for an message which includes the API Request details, including the full signature and headers of the request.
81 |
82 | > This message starts with POST /prod/ride then shows the headers of the request made.
83 |
84 | > You may notice that there were both x-amz-date and x-amz-security-token headers sent among other headers. These two headers are part of the overall request signature, along with the Authorization header.
85 |
86 | {{% /expand %}}
87 |
88 | If your API now invokes correctly and application functions as expected summoning unicorns again, you can proceed to the next module, IAM-based Authorization.
89 |
--------------------------------------------------------------------------------
/workshop/content/serverless/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Serverless API"
3 | chapter = true
4 | weight = 30
5 | pre = "2. "
6 | +++
7 |
8 | In this module, you will add a serverless backend to your Wild Rydes application leveraging [Amazon API Gateway](https://aws.amazon.com/api-gateway/) and [AWS Lambda](https://aws.amazon.com/lambda/). You will then enable authentication and authorization on your API to secure the backend to only accept valid, authorized requests.
--------------------------------------------------------------------------------
/workshop/content/serverless/architecture/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Solution Architecture"
3 | weight = 31
4 | +++
5 |
6 | Building on Module 1, this module will add a Serverless backend built using Amazon API Gateway and AWS Lambda. For persistence, you will use Amazon DynamoDB as a NoSQL data store. All of the above services are serverless so you can seamlessly scale your application as your demands grow. After creating the API, we will integrate our client application to call it via the AWS Amplify library.
7 |
8 | 
9 |
10 | ### Implementation Overview
11 |
12 | Each of the following sections provides an implementation overview and detailed, step-by-step instructions. The overview should provide enough context for you to complete the implementation if you're already familiar with the AWS Management Console or you want to explore the services yourself without following a walkthrough.
13 |
14 | If you're using the latest version of the Chrome, Firefox, or Safari web browsers the step-by-step instructions won't be visible until you expand the section.
--------------------------------------------------------------------------------
/workshop/content/serverless/backend/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Create Serverless API Backend"
3 | weight = 32
4 | +++
5 |
6 | You will be creating your Serverless API built with Amazon API Gateway, AWS Lambda, and Amazon DynamoDB via a CloudFormation template. Since this workshop is focused on authentication and authorization, this template will create the backend infrastructure, but not enable any security settings and the rest of the module will enable and configure such settings.
7 |
8 | ### High-Level Instructions
9 |
10 | Create a new WildRydes Serverless Backend stack by launching a CloudFormation stack based on the ***ServerlessBackend.yaml*** file in the module 2 folder. Name the stack `WildRydesBackend`.
11 |
12 | This WildRydes backend CloudFormation template will provision your API Gateway deployment with Lambda functions for compute, a DynamoDB database for persistence, and an S3 bucket for photo uploads which will be used in module 3. Additionally, the necessary function invocation permissions and execution role for the Lambda function will also be provisioned.
13 |
14 | Click on the link for the region you have chosen:
15 | {{< tabs name="Region" >}}
16 | {{{< tab name="N. Virginia (us-east-1)" include="us-east-1.md" />}}
17 | {{{< tab name="Ohio (us-east-2)" include="us-east-2.md" />}}
18 | {{{< tab name="Oregon (us-west-2)" include="us-west-2.md" />}}
19 | {{{< tab name="Ireland (eu-west-1)" include="eu-west-1.md" />}}
20 | {{{< tab name="Singapore (ap-southeast-1)" include="ap-southeast-1.md" />}}
21 | {{< /tabs >}}
22 |
23 | {{% expand "Step-by-step instructions (expand for details)" %}}
24 |
25 | 1. Launch the CloudFormation stack from the links above, choosing the link appropriate for the region you selected for this workshop. **Be sure to select the same region as you were using previously in this workshop to launch this CloudFormation stack**
26 |
27 | 1. On the next screen, Step 2, confirm the stack name is `WildRydesBackend` and click ***Next***.
28 |
29 | 1. On the Configure Stack Options page, accept all the defaults and click ***Next***.
30 |
31 | 1. Choose to ***Acknowledge that the CloudFormation template may create IAM resources with custom names***. Finally, click ***Create stack***.
32 |
33 | 1. It will take a few minutes for the Stack to create. Choose the ***Stack Info*** tab to go to the overall stack status page and wait until the stack is fully launched and shows a status of **CREATE_COMPLETE**. Click the refresh icon to see progress updates.
34 |
35 | 1. With the **WildRydesBackend** stack selected, click on the ***Outputs*** tab and copy the value shown for the **WildRydesApiInvokeUrl** to your Cloud9 scratchpad editor tab.
--------------------------------------------------------------------------------
/workshop/content/serverless/backend/ap-southeast-1.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Singapore"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Launch Serverless API backend in ap-southeast-1: [][Launch ap-southeast-1]
9 |
10 | [Launch ap-southeast-1]: https://console.aws.amazon.com/cloudformation/home?region=ap-southeast-1#/stacks/new?stackName=WildRydesBackend&templateURL={{%siteparam "artifactURLPrefix" %}}/ServerlessBackend.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/serverless/backend/eu-west-1.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Ireland"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Launch Serverless API backend in eu-west-1: [][Launch eu-west-1]
9 |
10 | [Launch eu-west-1]: https://console.aws.amazon.com/cloudformation/home?region=eu-west-1#/stacks/new?stackName=WildRydesBackend&templateURL={{%siteparam "artifactURLPrefix" %}}/ServerlessBackend.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/serverless/backend/us-east-1.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "N. Virginia"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Launch Serverless API backend in us-east-1: [][Launch us-east-1]
9 |
10 | [Launch us-east-1]: https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=WildRydesBackend&templateURL={{%siteparam "artifactURLPrefix" %}}/ServerlessBackend.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/serverless/backend/us-east-2.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Ohio"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Launch Serverless API backend in us-east-2: [][Launch us-east-2]
9 |
10 | [Launch us-east-2]: https://console.aws.amazon.com/cloudformation/home?region=us-east-2#/stacks/new?stackName=WildRydesBackend&templateURL={{%siteparam "artifactURLPrefix" %}}/ServerlessBackend.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/serverless/backend/us-west-2.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Oregon"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Launch Serverless API backend in us-west-2: [][Launch us-west-2]
9 |
10 | [Launch us-west-2]: https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?stackName=WildRydesBackend&templateURL={{%siteparam "artifactURLPrefix" %}}/ServerlessBackend.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/serverless/integration/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Integrate API into Wild Rydes"
3 | weight = 33
4 | +++
5 |
6 | Now that you have created our Serverless API, you need to update your Wild Rydes web application to integrate with it. You will leverage the AWS Amplify client library to make API calls and inject security seamlessly to support your authentication and authorization scenarios.
7 |
8 | ### High-Level Instructions
9 |
10 | First, expand your **amplify-config.js** file to store your new API Gateway endpoint. Next, within **MainApp.js** under pages, enable the **hasAPI** method by uncommenting its functionality. Additionally, update the **getData** method to capture the latitude and longitude selected on the map and send to the API as a PickupLocation object including both the latitude and longitude.
11 |
12 | {{% expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. First, you need to update the **/website/src/amplify-config.js** file to include your new API Gateway endpoint. Store the endpoint including the /prod at the end in the endpoint property under the **WildRydesAPI** section.
15 |
16 | > Do not change the name `WildRydesAPI` in this configuration file or later functionality in the workshop will not work. An example of the API configuration portion of the amplify-config file after updating the configuration properly is shown below:
17 |
18 | ```javascript
19 | API: {
20 | endpoints: [
21 | {
22 | name: 'WildRydesAPI',
23 | endpoint: 'https://1ngrgqjt6c.execute-api.us-east-1.amazonaws.com/prod',
24 | region: 'us-east-1'
25 | }
26 | ]
27 | },
28 | ```
29 |
30 | 1. Next, you need to enable the hasAPI method by uncommenting its code within **/website/src/pages/MainApp.js**.
31 |
32 | ```javascript
33 | hasApi() {
34 | const api = awsConfig.API.endpoints.filter(v => v.endpoint !== '');
35 | return (typeof api !== 'undefined');
36 | }
37 | ```
38 |
39 | 1. Finally, within the same file, we will implement the API request for a ride as a POST request to our API which sends a body containing the requested latitude and longitude as the pickup location. Update the **getData()** method to be as follows:
40 |
41 | ```javascript
42 | async getData(pin) {
43 | const apiRequest = {
44 | body: {
45 | PickupLocation: {
46 | Longitude: pin.longitude,
47 | Latitude: pin.latitude
48 | }
49 | },
50 | headers: {
51 | 'Authorization': '', // To be updated
52 | 'Content-Type': 'application/json'
53 | }
54 | };
55 | console.log('API Request:', apiRequest);
56 | return await API.post(apiName, apiPath, apiRequest);
57 | }
58 | ```
59 |
--------------------------------------------------------------------------------
/workshop/content/serverless/validation/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Validate API Functionality"
3 | weight = 34
4 | +++
5 |
6 | Now that you've integrated code changes to call your new Serverless API, you should test the end-to-end user experience to ensure the application is working correctly. The API currently requires no authentication so any request will currently be accepted until we enable required authentication.
7 |
8 | ### High-Level Instructions
9 |
10 | Go back to your browser tab with Wild Rydes running and sign-in again at `/signin`. Once signed in, click anywhere on the map to indicate a pickup location, then select the ***Request*** button to call your ride.
11 |
12 | You should be informed of your unicorn's arrival momentarily.
--------------------------------------------------------------------------------
/workshop/content/setup/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Setup"
3 | date = 2020-03-04
4 | chapter = true
5 | weight = 10
6 | pre = "0. "
7 | +++
8 |
9 | ### AWS Account
10 |
11 | In order to complete this workshop, you'll need an AWS account and access to create and manage AWS resources that are used in this workshop, including Cloud9, Cognito, API Gateway, Lambda, DynamoDB and IAM policies and roles.
12 | _The IAM user/role used for this workshop will require administrative and IAM full access permissions._
13 |
14 | If you currently don't have an AWS account, you can create one here: [https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account)
15 |
--------------------------------------------------------------------------------
/workshop/content/setup/cloud9/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Create a Cloud9 Workspace"
3 | weight = 13
4 | +++
5 |
6 | {{% notice tip %}}
7 | Ad blockers, javascript disablers, and tracking blockers should be disabled for
8 | the cloud9 domain, or connecting to the workspace might be impacted.
9 | Cloud9 requires third-party-cookies. You can whitelist the [specific domains]( https://docs.aws.amazon.com/cloud9/latest/user-guide/troubleshooting.html#troubleshooting-env-loading).
10 | {{% /notice %}}
11 |
12 | ### AWS Cloud9 IDE
13 |
14 | [AWS Cloud9](https://aws.amazon.com/cloud9/) is a cloud-based integrated development environment (IDE) that lets you write, run, and debug code from any machine with just a browser. It includes a code editor, debugger and terminal. Cloud9 comes pre-packaged with essential tools for popular programming languages and the AWS Command Line Interface (CLI) pre-installed so you don't need to install files or configure your laptop for this workshop.
15 |
16 | ### Launch Cloud9 IDE
17 |
18 | In this section you will launch a CloudFormation stack that will create a new [AWS VPC](https://aws.amazon.com/vpc) environment and a [Cloud9 IDE](https://aws.amazon.com/cloud9) instance that you will use in the rest of the workshop.
19 |
20 | Click on the link for the region you have chosen:
21 | {{< tabs name="Region" >}}
22 | {{{< tab name="N. Virginia (us-east-1)" include="us-east-1.md" />}}
23 | {{{< tab name="Ohio (us-east-2)" include="us-east-2.md" />}}
24 | {{{< tab name="Oregon (us-west-2)" include="us-west-2.md" />}}
25 | {{{< tab name="Ireland (eu-west-1)" include="eu-west-1.md" />}}
26 | {{{< tab name="Singapore (ap-southeast-1)" include="ap-southeast-1.md" />}}
27 | {{< /tabs >}}
28 |
29 | {{%expand "Step-by-step instructions (expand to see)" %}}
30 |
31 | 1. Launch the CloudFormation stack from the links above, choosing the link appropriate for the region you selected for this workshop.
32 |
33 | 2. On the next screen, Step 2, confirm the stack name is `WildRydes-Cloud9` and click **Next**.
34 |
35 | 3. On the Configure Stack Options page, accept all the defaults and click **Next**.
36 |
37 | 4. On the Review page, review the summary details then click **Create stack**.
38 |
39 | 5. It will take a few minutes for the stack to create. Choose the **Stack Info** tab to go to the overall stack status page and wait until the stack is fully launched and shows a status of *CREATE_COMPLETE*. Click the refresh icon periodically to see progress update.
40 |
41 | 6. With the *WildRydes-Cloud9* stack selected, click on the **Outputs** tab and copy the value shown for the *Cloud9IDE* to the clipboard. Browse to that URL in a new browser tab to load your IDE environment.
42 |
43 | > Note: When you launch the stack, CloudFormation deploys a nested CloudFormation stack to launch the Cloud9 resources. You can safely ignore that template which is prefixed with "aws-cloud9-WildRydes-".
44 |
45 | 
46 |
47 | {{% /expand%}}
48 |
49 | Once you have launched and navigated to your Cloud9 workspace URL shown in your CloudFormation stack outputs, you should have an IDE environment as shown below:
50 |
51 | 
52 |
--------------------------------------------------------------------------------
/workshop/content/setup/cloud9/ap-southeast-1.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Singapore"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Create a Cloud9 environment in ap-southeast-1: [][Launch ap-southeast-1]
9 |
10 | [Launch ap-southeast-1]: https://console.aws.amazon.com/cloudformation/home?region=ap-southeast-1#/stacks/new?stackName=WildRydes-Cloud9&templateURL=https://s3.amazonaws.com/wildrydes-ap-southeast-1/Auth/0_GettingStarted/Cloud9WithNewVPC.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/setup/cloud9/eu-west-1.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Ireland"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Create a Cloud9 environment in eu-west-1: [][Launch eu-west-1]
9 |
10 | [Launch eu-west-1]: https://console.aws.amazon.com/cloudformation/home?region=eu-west-1#/stacks/new?stackName=WildRydes-Cloud9&templateURL=https://s3.amazonaws.com/wildrydes-eu-west-1/Auth/0_GettingStarted/Cloud9WithNewVPC.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/setup/cloud9/us-east-1.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "N. Virginia"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Create a Cloud9 environment in us-east-1: [][Launch us-east-1]
9 |
10 | [Launch us-east-1]: https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=WildRydes-Cloud9&templateURL={{%siteparam "artifactUrlPrefix" %}}/Cloud9WithNewVPC.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/setup/cloud9/us-east-2.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Ohio"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Create a Cloud9 environment in us-east-2: [][Launch us-east-2]
9 |
10 | [Launch us-east-2]: https://console.aws.amazon.com/cloudformation/home?region=us-east-2#/stacks/new?stackName=WildRydes-Cloud9&templateURL=https://s3.amazonaws.com/wildrydes-us-east-2/Auth/0_GettingStarted/Cloud9WithNewVPC.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/setup/cloud9/us-west-2.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Oregon"
3 | chapter = false
4 | disableToc = true
5 | hidden = true
6 | +++
7 |
8 | Create a Cloud9 environment in us-west-2: [][Launch us-west-2]
9 |
10 | [Launch us-west-2]: https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?stackName=WildRydes-Cloud9&templateURL=https://s3.amazonaws.com/wildrydes-us-west-2/Auth/0_GettingStarted/Cloud9WithNewVPC.yaml
11 |
--------------------------------------------------------------------------------
/workshop/content/setup/setupc9/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Setup Cloud9 Workspace"
3 | weight = 14
4 | +++
5 |
6 | In the ***bash*** terminal in Cloud9, you can run AWS CLI commands just like you would on your local computer. Verify that your user is logged in by running the following:
7 | ```plaintext
8 | aws sts get-caller-identity
9 | ```
10 |
11 | You should see output indicating your account and user information:
12 |
13 | ```JSON
14 | {
15 | "Account": "123456789012",
16 | "UserId": "AKIAI44QH8DHBEXAMPLE",
17 | "Arn": "arn:aws:iam::123456789012:user/Alice"
18 | }
19 | ```
20 |
21 | Keep your AWS Cloud9 IDE opened in a tab throughout this workshop as you'll be using it for most activities.
22 |
23 | ### Expand Cloud9 storage
24 |
25 | In order to accomodate dependencies, the underlying drive has already been expanded and we only need to increase the partition size with these 2 commands:
26 |
27 | ```plaintext
28 | sudo growpart /dev/xvda 1
29 | sudo resize2fs /dev/xvda1
30 | ```
31 |
32 | ### Download Workshop Code
33 |
34 | Download the WildRydes website artifacts to your Cloud9 IDE environment by running the following command in the Cloud9 terminal window:
35 |
36 | ```plaintext
37 | curl -O {{% siteparam "artifactUrlPrefix" %}}/website.tar.gz
38 | tar xvf website.tar.gz
39 | ```
40 |
41 | ### Initialize your developer workspace
42 |
43 | 1. Run the following commands to upgrade your Node.js version to the latest version of Node.js 10. The [AWS Amplify](https://aws-amplify.github.io/) Javascript library which we will be using requires Node.js 10 or higher.
44 |
45 | ```plaintext
46 | nvm i 16
47 | nvm alias default 16
48 | ```
49 |
50 | 2. Install the yarn package manager and website dependencies by running the following commands:
51 |
52 | ```plaintext
53 | npm install -g yarn
54 | cd ~/environment/website/
55 | yarn install
56 | ```
57 |
58 | {{% notice tip %}}
59 | Keep an open scratch pad in Cloud9 or a text editor on your local computer for notes. When the step-by-step directions tell you to note something such as an ID or Amazon Resource Name (ARN), copy and paste that into the scratch pad tab.
60 | {{% /notice %}}
61 |
62 |
--------------------------------------------------------------------------------
/workshop/content/user_auth/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "User Authentication"
3 | chapter = true
4 | weight = 20
5 | pre = "1. "
6 | +++
7 |
8 | In this module, you will create an Amazon Cognito User Pool and Identity Pool for the Wild Rydes application. The Cognito User Pool will store user profile information and provide sign-up and sign-in capabilities, with the Cognito Identity Pool providing the ability to assume an Identity and Access Management (IAM) role from within the application.
9 |
10 | Since Wild Rydes is a ride sharing application, a key requirement is that all users must sign-up and sign-in before they're allowed to request a ride. You will configure the application to integrate with [Amazon Cognito](https://aws.amazon.com/cognito/) for these purposes via the [AWS Amplify](https://aws-amplify.github.io/) JavaScript library.
--------------------------------------------------------------------------------
/workshop/content/user_auth/architecture/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Solution Architecture"
3 | weight = 21
4 | +++
5 |
6 | The architecture for this module is very straightforward. All of your static web content including HTML, CSS, JavaScript, images and other files will be served locally from your Cloud9 workspace. As you make changes to the website application code, all changes will be automatically updated and shown in your browser via live reload capabilities.
7 |
8 | For this module, we will be creating a Cognito User Pool as our secure user directory then configuring our application to use the AWS Amplify library to easily integrate Amazon Cognito into our application.
9 |
10 | 
11 |
12 | ### Implementation Overview
13 |
14 | Each of the following sections provides an implementation overview and detailed, step-by-step instructions. The high-level overview should provide context for you to complete the implementation yourself if you're comfortable with the AWS Management Console and are comfortable exploring the services and documentation yourself without following a walkthrough.
15 |
16 | If you're using the latest version of the Chrome, Firefox, or Safari web browsers the step-by-step instructions won't be visible until you expand the section.
17 |
18 |
--------------------------------------------------------------------------------
/workshop/content/user_auth/identity_pool/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Creating a Cognito Identity Pool"
3 | weight = 24
4 | +++
5 |
6 | Cognito Identity Pools are used to provide AWS credentials via IAM roles to end-user applications. Since we'll be integrating our Cognito deployment and users with other AWS services, we'll go ahead and create this identity pool now.
7 |
8 | ### High-Level Instructions
9 |
10 | You will need to create a Cognito Identity Pool linked to the Cognito User Pool and app client ID you just created. Your application will not require un-authenticated users to access any AWS resources, so you do not need to enable access to unauthenticated identities.
11 |
12 | {{%expand "Step-by-step instructions (expand for details)" %}}
13 |
14 | 1. In the Cognito console, choose **Federated Identities** in the header bar to switch to the console for Cognito Federated Identities.
15 |
16 | 2. Choose **Create new identity pool**.
17 |
18 | 3. Input `wildrydes_identity_pool` as the Identity pool name.
19 |
20 | 4. Expand **Authentication providers**.
21 |
22 | 5. Within the Cognito tab, input the User Pool ID and App client Id you copied previously to the scratchpad tab.
23 |
24 | 
25 |
26 | 6. Choose **Create Pool**.
27 |
28 | 7. Choose **Allow** to allow Cognito Identity Pools to setup IAM roles for your application's users. Permissions and settings of these roles can be customized later.
29 |
30 | 8. Copy/paste the ***Identity Pool ID***, highlighted in red within the code sample in the Get AWS Credentials section, into your Cloud9 scatchpad editor tab.
31 |
32 | > Do not copy the quatation marks, but include the region code and ":" character.
33 |
34 | 
35 |
36 | 9. Your scratchpad should now have values for the following Cognito resources:
37 |
38 | 
39 |
40 | {{% /expand %}}
--------------------------------------------------------------------------------
/workshop/content/user_auth/local_web/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Running the website locally"
3 | chapter = false
4 | weight = 22
5 | +++
6 |
7 | 1. From your Cloud9 workspace, select the terminal window and when you are within your ***~/environment/website/*** directory, run the following command to start the local web server
8 |
9 | ```bash
10 | yarn start
11 | ```
12 |
13 | Wait for the development server to start. You can ignore any message saying ***Compiled with warnings*** as we will resolve these warnings as we add our functionality to the application.
14 |
15 | 2. Now that the development server has started, click **Preview Running Application** in the top of the screen next to the Run button.
16 |
17 | 
18 |
19 | 3. The web application will load in a small window next to the terminal at the bottom of the Cloud9 IDE. Click the **re-size** button next to the word **Browser** to open this window in a new tab.
20 |
21 | 
22 |
23 | As you make changes to the web application, this tab will automatically refresh to reflect your changes. Leave this tab open and return to the Cloud9 IDE tab to continue the workshop.
24 |
25 | Though the Wild Rydes website may look functional, there is currently no integration for sign-up or sign-in requests to go anywhere.
26 |
--------------------------------------------------------------------------------
/workshop/content/user_auth/user_pool/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Creating Cognito User Pool"
3 | chapter = false
4 | weight = 23
5 | +++
6 |
7 | Amazon Cognito User Pools lets you add user sign-up and sign-in capabilities to your web and mobile apps quickly and easily. In this step, we'll create a Cognito user pool for our Wild Rydes app.
8 |
9 | ### High-Level Instructions
10 |
11 | Use the AWS console to create an Amazon Cognito User Pool requiring e-mail verification
12 |
13 | {{% notice tip%}}
14 | The console's region will default to the last region you were using previously. Change this to the same region where you launched your Cloud9 environment previously.
15 | {{% /notice %}}
16 |
17 | {{% expand "Step-by-Step instructions (expand for details)" %}}
18 | 1. In the AWS Management Console choose **Services** then select **Cognito** under Security, Identity, and Compliance.
19 |
20 | 2. Choose your desired **Region** in top-right of the console if not already selected. This should match the region where you launched your Cloud9 environment previously.
21 |
22 | 3. Choose **Create User Pool**.
23 |
24 | 
25 |
26 | 4. Under **Cognito user pool sign-in options**, check User name, Email, and Phone Number checkboxes, then select **Next**.
27 |
28 | 5. Leave the Password Policy defaults, then scroll down to Multi-factor authentication and select **No MFA**.
29 |
30 | 
31 |
32 | 6. Leave all other defaults settings, then select **Next**.
33 |
34 | 7. On the **Configure sign-up experience** page, leave all defaults and select **Next**.
35 |
36 | 8. On the **Configure message delivery** page, select **Send email with Cognito**.
37 |
38 | 
39 |
40 | 9. Scroll down to the **SMS** section, then select **Create a new IAM role** and enter **wildrydes-cognito-messages** for the IAM role name. Then select **Next**.
41 |
42 | 
43 |
44 | 10. Next, enter `WildRydes` as the **User pool name**.
45 |
46 | 11. Next, scroll down to the **Initial app client** section and leave Public client selected. For App client name, enter `wildrydes-web-app`.
47 |
48 | 12. Make sure **Don't generate a client secret** is selected, then click **Next**
49 |
50 | 13. Review the summary of all provided settings for accuracy then choose **Create user pool**.
51 |
52 | 14. Within Cloud9, click the + symbol and choose to create **New File**. You will use this new blank editor tab as a scratchpad for various resource names and variables.
53 |
54 | 
55 |
56 | 15. Back in the AWS Cognito console, copy your new User Pool Id into the scratchpad tab.
57 |
58 | 
59 |
60 | 16. Click the **App Integration** tab and scroll all the way down to the bottom. Then copy the Client ID into the scratch tab.
61 |
62 | 
63 |
64 | 17. Copy the ***App client ID*** over to your scratchpad. You will be using both of these values later on.
65 |
66 | 
67 |
68 | {{% /expand %}}
--------------------------------------------------------------------------------
/workshop/content/user_auth/validation/_index.en.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "Validating sign-up and sign-in"
3 | weight = 26
4 | +++
5 |
6 | Now that you have integrated our Amplify code into our application, you need to test the site to see that authentication is working end-to-end.
7 |
8 | ### High-Level Instructions
9 |
10 | Return to your browser tab where you started your Wild Rydes application earlier after popping out from the Cloud9 IDE once in preview mode. This page automatically refreshes after you save any code changes so should now reflect all of your changes and be ready for testing.
11 |
12 | Visit the ***/register*** path to sign-up as a new user, providing a valid phone number with `+country_code` first preceeding the number. For a US-based phone number, an example would be `+14251234567`. You should then see that a verification message is sent with a one-time code upon registering, which is required before you're able to sign-in.
13 |
14 | After signing up as a new user, sign-in with the same user at the ***/signin*** path. If the page loads a map, sign-in was successful and you have successfully integrated Cognito for app authentication.
15 |
16 | {{% expand "Step-by-step instructions (expand for details)" %}}
17 |
18 | 1. Visit `/register` path of your Cloud9's website to go to the registration page.
19 |
20 | 2. Input your e-mail address, phone number with `+country_code` first preceeding the number, as well as your password twice. For a US-based phone number, an example would be `+14251234567`.
21 |
22 | > Your password must include 8 characters, including uppercase and lowercase characters, and at least 1 number and 1 special character.
23 |
24 | 3. Choose **Let's Ryde** to submit registration.
25 |
26 | 4. On the verify e-mail screen, enter the one-time code sent to your e-mail address provided then choose **Verify**.
27 |
28 | > Be sure to check your spam folder for the e-mail with your verification code if you do not see it in your inbox.
29 |
30 | 5. Assuming no errors were encountered, you will be redirected to the Sign-in screen. Now, re-enter the same e-mail address and password you chose at registration.
31 |
32 | 6. If the page then loads a map, sign-in was successful and you have successfully integrated Cognito for app authentication.
33 |
34 | 7. Optionally, you may scroll down beyond the map to copy your user's identity token and decode it by pasting it into the 'encoded' input box at [JWT.io](http://jwt.io/). You will see all of your user's attributes are encoded within the token, along with other standard attributes such as the time the token was issued, the time the token expires, the user's unique ID, and more.
35 |
36 |
--------------------------------------------------------------------------------
/workshop/layouts/partials/favicon.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/workshop/layouts/partials/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ .Hugo.Generator }}
11 | {{ partial "meta.html" . }}
12 | {{ partial "favicon.html" . }}
13 | {{ .Title }} :: {{ .Site.Title }}
14 |
15 | {{ $assetBusting := not .Site.Params.disableAssetsBusting }}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{with .Site.Params.themeVariant}}
27 |
28 | {{end}}
29 |
30 |
31 |
32 |
35 |
45 | {{ partial "custom-header.html" . }}
46 |
47 |
48 |
49 | {{ partial "menu.html" . }}
50 |
51 |
52 |