├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── gatsby-ssr.js ├── package.json ├── src ├── assets │ ├── images │ │ └── gatsby-icon.png │ └── vectors │ │ ├── amplify.svg │ │ ├── cognito.svg │ │ └── gatsby.svg ├── components │ ├── Auth │ │ ├── AppUser.js │ │ └── index.js │ ├── Forms │ │ ├── AuthForms.js │ │ └── index.js │ ├── Layout │ │ ├── AppLayouts.js │ │ └── index.js │ ├── Nav │ │ ├── Nav.js │ │ ├── UserNav.js │ │ └── index.js │ ├── Pages │ │ ├── Home.js │ │ ├── IndexPage.js │ │ ├── Profile.js │ │ ├── Reset.js │ │ ├── SignIn.js │ │ ├── SignUp.js │ │ └── index.js │ └── Routes │ │ ├── PrivateRoute.js │ │ └── PublicRoute.js ├── pages │ ├── 404.js │ └── app.js └── scss │ ├── colors.scss │ ├── fonts.scss │ └── styles.scss └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | .cache 3 | node_modules 4 | yarn-error.log 5 | 6 | # Build directory 7 | /public 8 | .DS_Store 9 | 10 | # misc 11 | z-archive/* 12 | .vscode/ 13 | 14 | #amplify 15 | amplify/* 16 | build/ 17 | dist/ 18 | node_modules/ 19 | aws-exports.js 20 | awsconfiguration.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 gatsbyjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gatsby Authentication with AWS Amplify 2 | 3 | Note: this auth starter was adopted from the original starter by [dabit3](https://github.com/dabit3/gatsby-auth-starter-aws-amplify). 4 | 5 | This auth starter implements a basic authentication flow for signing up signing in users as well as protected client side routing using [AWS Amplify](https://amplify.aws). Auth features: 6 | 7 | - User sign up 8 | - User sign in 9 | - Multi-factor Authentication 10 | - User sign-out 11 | - Password Reset 12 | - Error Feedback 13 | 14 | # Run locally 15 | 16 | 1. Create the project 17 | 18 | ```sh 19 | gatsby new new-auth-site https://github.com/ben-siewert/gatsby-starter-auth-aws-amplify 20 | ``` 21 | 22 | 2. Change into the new directory 23 | 24 | ```sh 25 | cd new-auth-site 26 | ``` 27 | 28 | 3. Install dependencies 29 | 30 | ```sh 31 | yarn 32 | # or 33 | npm install 34 | ``` 35 | 36 | 4. Install & configure the AWS Amplify CLI. 37 | 38 | ```sh 39 | npm install -g @aws-amplify/cli 40 | 41 | amplify configure 42 | ``` 43 | 44 | 5. Create a new AWS Amplify Project 45 | 46 | ``` 47 | amplify init 48 | ``` 49 | 50 | > Here, walk through the following steps: 51 | 52 | - Enter a name for the project **YOURPROJECTNAME** 53 | - Enter a name for the environment **master** 54 | - Choose your default editor: **Visual Studio Code** (or your editor of choice) 55 | - Choose the type of app that you're building **javascript** 56 | - What javascript framework are you using **react** 57 | - Source Directory Path: **src** 58 | - Distribution Directory Path: **public** 59 | - Build Command: **npm run build** 60 | - Start Command: **npm run dev** 61 | 62 | The CLI will then initialize a project in the cloud. 63 | 64 | 6. Add Auth to the Ampliy project it will configure a CloudFormation template that has an Amazon Cognito resource that enables user authentication. 65 | 66 | ```sh 67 | amplify add auth 68 | ``` 69 | 70 | > Here, walk through the following steps: 71 | 72 | - Default configuration: **(recommended)** 73 | - How do you want users to be able to sign in?: **recommended: (Email) or (Email and Phone Number)** 74 | - Do you want to configure advanced settings?: **recommended: (NO, I am done.) or (Yes, I want to make some additional changes)** 75 | 76 | 7. Finally, Push the updated project configuration to AWS. It will deploy a CloudFormation template that has an Amazon Cognito resource that enables user authentication. 77 | 78 | ```sh 79 | amplify push 80 | ``` 81 | 82 | 8. Then you can run it by: 83 | 84 | ```sh 85 | gatsby develop 86 | ``` 87 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Browser APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/browser-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it 8 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: 'Gatsby Auth Starter AWS Amplify', 4 | }, 5 | plugins: [ 6 | { 7 | resolve: 'gatsby-source-filesystem', 8 | options: { 9 | path: `${__dirname}/src/assets/images`, 10 | name: 'images', 11 | }, 12 | }, 13 | { 14 | resolve: 'gatsby-plugin-react-svg', 15 | options: { 16 | rule: { 17 | include: /vectors/, 18 | }, 19 | }, 20 | }, 21 | `gatsby-plugin-netlify`, 22 | `gatsby-plugin-sass`, 23 | 'gatsby-plugin-sharp', 24 | 'gatsby-transformer-sharp', 25 | { 26 | resolve: `gatsby-plugin-google-analytics`, 27 | options: { 28 | trackingId: "UA-145157132-1", 29 | // Defines where to place the tracking script - `true` in the head and `false` in the body 30 | head: false, 31 | // Setting this parameter is optional 32 | anonymize: true, 33 | // Setting this parameter is also optional 34 | respectDNT: true, 35 | // Delays sending pageview hits on route update (in milliseconds) 36 | pageTransitionDelay: 0, 37 | sampleRate: 5, 38 | siteSpeedSampleRate: 10, 39 | cookieDomain: "https://authenticaysh.netlify.com/", 40 | }, 41 | }, 42 | ], 43 | } 44 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Node APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/node-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it 8 | exports.onCreatePage = async ({ page, actions }) => { 9 | const { createPage } = actions 10 | 11 | // page.matchPath is a special key that's used for matching pages 12 | // only on the client. 13 | if (page.path.match(/^\/app/)) { 14 | page.matchPath = `/*` 15 | 16 | // Update the page. 17 | createPage(page) 18 | } 19 | } 20 | 21 | exports.onCreateWebpackConfig = ({ getConfig, stage }) => { 22 | const config = getConfig() 23 | if (stage.startsWith('develop') && config.resolve) { 24 | config.resolve.alias = { 25 | ...config.resolve.alias, 26 | 'react-dom': '@hot-loader/react-dom', 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's SSR (Server Side Rendering) APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/ssr-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-auth-starter-aws", 3 | "description": "Gatsby authentication starter for AWS", 4 | "version": "1.0.0", 5 | "author": "Ben Siewert", 6 | "dependencies": { 7 | "aws-amplify": "^1.1.34", 8 | "aws-amplify-react": "2.1.5", 9 | "gatsby": "^2.13.50", 10 | "gatsby-image": "^2.2.8", 11 | "gatsby-plugin-create-client-paths": "^2.1.3", 12 | "gatsby-plugin-google-analytics": "^2.1.7", 13 | "gatsby-plugin-netlify": "^2.1.3", 14 | "gatsby-plugin-react-svg": "^2.1.1", 15 | "gatsby-plugin-sass": "^2.1.4", 16 | "gatsby-plugin-sharp": "^2.2.9", 17 | "gatsby-source-filesystem": "^2.1.8", 18 | "gatsby-transformer-sharp": "^2.2.5", 19 | "react": "^16.4.1", 20 | "react-dom": "^16.8.6", 21 | "react-number-format": "^4.0.8" 22 | }, 23 | "keywords": [ 24 | "gatsby" 25 | ], 26 | "license": "MIT", 27 | "scripts": { 28 | "clean": "rimraf .cache public", 29 | "build": "gatsby build", 30 | "dev": "npm run clean && gatsby develop", 31 | "start": "gatsby develop", 32 | "format": "prettier --write '**/*.js'", 33 | "test": "echo \"Error: no test specified\" && exit 1" 34 | }, 35 | "devDependencies": { 36 | "@hot-loader/react-dom": "^16.8.6", 37 | "bootstrap": "^4.3.1", 38 | "modern-normalize": "^0.5.0", 39 | "node-sass": "^4.13.1", 40 | "prettier": "^1.13.7", 41 | "rimraf": "^2.6.3" 42 | }, 43 | "repository": { 44 | "type": "git", 45 | "url": "https://github.com/gatsbyjs/gatsby-starter-default" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/assets/images/gatsby-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shpeedle/gatsby-starter-auth-aws-amplify/c4f1a32f9c8ce69b8db6175c8a38e2ff75e548c1/src/assets/images/gatsby-icon.png -------------------------------------------------------------------------------- /src/assets/vectors/amplify.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Logo/Amplify Logo White 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/vectors/cognito.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/vectors/gatsby.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/Auth/AppUser.js: -------------------------------------------------------------------------------- 1 | const isBrowser = typeof window !== `undefined` 2 | 3 | export const setUser = user => 4 | (window.localStorage.gatsbyUser = JSON.stringify(user)) 5 | 6 | const getUser = () => { 7 | if (window.localStorage.gatsbyUser) { 8 | let user = JSON.parse(window.localStorage.gatsbyUser) 9 | return user ? user : {} 10 | } 11 | return {} 12 | } 13 | 14 | export const isLoggedIn = () => { 15 | if (!isBrowser) return false 16 | 17 | const user = getUser() 18 | if (user) return !!user.username 19 | } 20 | 21 | export const getCurrentUser = () => isBrowser && getUser() 22 | 23 | export const logout = callback => { 24 | if (!isBrowser) return 25 | setUser({}) 26 | callback() 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Auth/index.js: -------------------------------------------------------------------------------- 1 | import * as AppUser from './AppUser' 2 | 3 | export { 4 | AppUser 5 | } -------------------------------------------------------------------------------- /src/components/Forms/AuthForms.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export function AuthForm({ children, title, error }) { 4 | return ( 5 |
6 |
7 |
8 |

{title}

9 | {error && ( 10 |

11 | {error.message ? error.message : error} 12 |

13 | )} 14 | {children} 15 |
16 |
17 |
18 | ) 19 | } 20 | 21 | export function Email({ handleUpdate, email, autoComplete }) { 22 | return ( 23 |
24 | 25 | 36 |
37 | ) 38 | } 39 | 40 | export function Password({ handleUpdate, password, autoComplete }) { 41 | return ( 42 |
43 | 44 | 54 |
55 | ) 56 | } 57 | 58 | export function ConfirmationCode({ handleUpdate, auth_code, autoComplete }) { 59 | return ( 60 |
61 | 62 | 72 |
73 | ) 74 | } 75 | -------------------------------------------------------------------------------- /src/components/Forms/index.js: -------------------------------------------------------------------------------- 1 | import { AuthForm, Email, Password, ConfirmationCode } from './AuthForms' 2 | 3 | export { AuthForm, Email, Password, ConfirmationCode } 4 | -------------------------------------------------------------------------------- /src/components/Layout/AppLayouts.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import 'modern-normalize/modern-normalize.css' 3 | import { Nav, UserNav } from '../Nav' 4 | import '../../scss/styles.scss' 5 | 6 | export function Layout({ children, isUserNav }) { 7 | return ( 8 |
9 | {isUserNav ? :
12 | ) 13 | } 14 | 15 | export function AppContent({ children }) { 16 | return ( 17 |
18 |
{children}
19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/Layout/index.js: -------------------------------------------------------------------------------- 1 | import { Layout, AppContent } from './AppLayouts' 2 | export { Layout, AppContent } 3 | -------------------------------------------------------------------------------- /src/components/Nav/Nav.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | 4 | function Nav() { 5 | return ( 6 | 30 | ) 31 | } 32 | 33 | export default Nav 34 | -------------------------------------------------------------------------------- /src/components/Nav/UserNav.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import { navigate } from '@reach/router' 4 | import { Auth } from 'aws-amplify' 5 | import { AppUser } from '../Auth' 6 | 7 | function UserNav() { 8 | const { logout } = AppUser 9 | function logOut(e) { 10 | e.preventDefault() 11 | 12 | Auth.signOut() 13 | .then(logout(() => navigate('/signin'))) 14 | .catch(err => console.log('error: ', err)) 15 | } 16 | 17 | return ( 18 | 44 | ) 45 | } 46 | 47 | export default UserNav 48 | -------------------------------------------------------------------------------- /src/components/Nav/index.js: -------------------------------------------------------------------------------- 1 | import UserNav from './UserNav' 2 | import Nav from './Nav' 3 | 4 | export { UserNav, Nav } 5 | -------------------------------------------------------------------------------- /src/components/Pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import { AppContent } from '../Layout' 4 | 5 | const Home = () => { 6 | return ( 7 |
8 | 9 |

Here's the App Home Page

10 |

11 | Since you are now logged in, view your profile: View profile 12 |

13 |

14 | This starter was built using AWS Amplify. Try it out:{' '} 15 | AWS Amplify 16 |

17 |
18 |
19 | ) 20 | } 21 | 22 | export default Home 23 | -------------------------------------------------------------------------------- /src/components/Pages/IndexPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import GatsbyIcon from '../../assets/vectors/gatsby.svg' 3 | import AmplifyIcon from '../../assets/vectors/amplify.svg' 4 | import CognitoIcon from '../../assets/vectors/cognito.svg' 5 | 6 | const IndexPage = () => { 7 | return ( 8 |
17 | 18 |

+

19 | 20 |

+

21 | 22 |
23 | ) 24 | } 25 | 26 | export default IndexPage 27 | -------------------------------------------------------------------------------- /src/components/Pages/Profile.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | 4 | import { getCurrentUser } from '../Auth/AppUser' 5 | import { AppContent } from '../Layout' 6 | 7 | const Profile = () => { 8 | const user = getCurrentUser() 9 | return ( 10 |
11 | 12 |

Here's the Profile Page

13 |

Email: {user.email}

14 |

Phone: {user.phone_number}

15 | Home 16 |
17 |
18 | ) 19 | } 20 | 21 | export default Profile 22 | -------------------------------------------------------------------------------- /src/components/Pages/Reset.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import { navigate } from '@reach/router' 4 | import { Auth } from 'aws-amplify' 5 | 6 | import { AuthForm, Email, Password, ConfirmationCode } from '../Forms' 7 | 8 | const initialState = { 9 | email: ``, 10 | auth_code: ``, 11 | password: ``, 12 | error: ``, 13 | loading: false, 14 | stage: 0, 15 | } 16 | 17 | class Reset extends React.Component { 18 | state = initialState 19 | 20 | handleUpdate = event => { 21 | this.setState({ 22 | [event.target.name]: event.target.value, 23 | error: '', 24 | }) 25 | } 26 | 27 | reset = async e => { 28 | e.preventDefault() 29 | const { email } = this.state 30 | try { 31 | this.setState({ loading: true }) 32 | await Auth.forgotPassword(email) 33 | console.log('forgotPassword') 34 | this.setState({ loading: false, stage: 1 }) 35 | } catch (err) { 36 | this.setState({ error: err, loading: false }) 37 | console.log('error...: ', err) 38 | } 39 | } 40 | 41 | confirmReset = async e => { 42 | e.preventDefault() 43 | const { email, auth_code, password } = this.state 44 | this.setState({ loading: true }) 45 | Auth.forgotPasswordSubmit(email, auth_code, password) 46 | .then(data => { 47 | console.log(data) 48 | this.setState({ loading: false }) 49 | }) 50 | .then(() => navigate('/signin')) 51 | .catch(err => { 52 | console.log(err) 53 | this.setState({ error: err, loading: false }) 54 | }) 55 | } 56 | 57 | render() { 58 | if (this.state.stage === 0) { 59 | return ( 60 | 61 | 66 | 81 |

82 | Back to Sign In 83 |

84 |
85 | ) 86 | } 87 | 88 | return ( 89 | 90 | 91 | 96 | 101 | 106 |

107 | Back to Sign In 108 |

109 | 124 |
125 |
132 |

136 | Lost your code? 137 |

138 | 145 |
146 |
147 | ) 148 | 149 | // return ( 150 | //
151 | //
152 | // {this.state.stage === 0 && ( 153 | //
154 | //

Reset your password

155 | // {this.state.error && ( 156 | //

157 | // {this.state.error.message 158 | // ? this.state.error.message 159 | // : this.state.error} 160 | //

161 | // )} 162 | //
163 | // 164 | // 173 | //
174 | // 189 | //

190 | // Back to Sign In 191 | //

192 | //
193 | // )} 194 | // {this.state.stage === 1 && ( 195 | //
196 | //
197 | //

Confirm Password Update

198 | // {this.state.error && ( 199 | //

200 | // {this.state.error.message 201 | // ? this.state.error.message 202 | // : this.state.error} 203 | //

204 | // )} 205 | //
206 | // 207 | // 217 | //
218 | //
219 | // 220 | // 228 | //
229 | //
230 | // 231 | // 240 | //
241 | // 256 | //
257 | //
264 | //

268 | // Lost your code? 269 | //

270 | // 277 | //
278 | //
279 | // )} 280 | //
281 | //
282 | // ) 283 | } 284 | } 285 | 286 | export default Reset 287 | -------------------------------------------------------------------------------- /src/components/Pages/SignIn.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import { navigate } from '@reach/router' 4 | import { Auth } from 'aws-amplify' 5 | 6 | import { AppUser } from '../Auth' 7 | import { AuthForm, Email, Password } from '../Forms' 8 | 9 | class SignIn extends React.Component { 10 | state = { 11 | username: ``, 12 | email: ``, 13 | password: ``, 14 | error: ``, 15 | loading: false, 16 | } 17 | 18 | handleUpdate = event => { 19 | if (event.target.name === 'email') { 20 | this.setState({ 21 | [event.target.name]: event.target.value, 22 | username: event.target.value, 23 | error: '', 24 | }) 25 | } 26 | this.setState({ 27 | [event.target.name]: event.target.value, 28 | error: '', 29 | }) 30 | } 31 | 32 | login = async e => { 33 | const { setUser } = AppUser 34 | e.preventDefault() 35 | const { username, password } = this.state 36 | try { 37 | this.setState({ loading: true }) 38 | await Auth.signIn(username, password) 39 | const user = await Auth.currentAuthenticatedUser() 40 | const userInfo = { 41 | ...user.attributes, 42 | username: user.username, 43 | } 44 | setUser(userInfo) 45 | this.setState({ loading: false }) 46 | navigate('/home') 47 | } catch (err) { 48 | this.setState({ error: err, loading: false }) 49 | console.log('error...: ', err) 50 | } 51 | } 52 | 53 | render() { 54 | return ( 55 | 56 | 61 | 66 |

67 | Forgot your password? Reset password 68 |

69 | 84 |

85 | No account? Create account 86 |

87 |
88 | ) 89 | } 90 | } 91 | 92 | export default SignIn 93 | -------------------------------------------------------------------------------- /src/components/Pages/SignUp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { navigate } from '@reach/router' 3 | import { Link } from 'gatsby' 4 | import { Auth } from 'aws-amplify' 5 | import NumberFormat from 'react-number-format' 6 | 7 | import { AuthForm, Email, Password, ConfirmationCode } from '../Forms' 8 | 9 | const initialState = { 10 | username: ``, 11 | password: ``, 12 | email: '', 13 | phone_number: '', 14 | auth_code: '', 15 | stage: 0, 16 | error: '', 17 | loading: false, 18 | } 19 | 20 | class SignUp extends React.Component { 21 | state = initialState 22 | 23 | handleUpdate = event => { 24 | if (event.target.name === 'email') { 25 | this.setState({ 26 | [event.target.name]: event.target.value, 27 | username: event.target.value, 28 | error: '', 29 | }) 30 | } 31 | if (event.target.name === 'phone_number') { 32 | this.setState({ 33 | [event.target.name]: event.target.value.replace(/\D/g, ''), 34 | error: '', 35 | }) 36 | } 37 | this.setState({ 38 | [event.target.name]: event.target.value, 39 | error: '', 40 | }) 41 | } 42 | 43 | signUp = async e => { 44 | e.preventDefault() 45 | const { username, password, email, phone_number } = this.state 46 | this.setState({ loading: true }) 47 | try { 48 | await Auth.signUp({ 49 | username, 50 | password, 51 | attributes: { email, phone_number }, 52 | }) 53 | this.setState({ stage: 1, loading: false }) 54 | } catch (err) { 55 | this.setState({ error: err, loading: false }) 56 | console.log('error signing up...', err) 57 | } 58 | } 59 | 60 | resendCode = async e => { 61 | e.preventDefault() 62 | const { email } = this.state 63 | this.setState({ loading: true }) 64 | try { 65 | await Auth.resendSignUp(email) 66 | this.setState({ stage: 1, loading: false }) 67 | } catch (err) { 68 | this.setState({ error: err, loading: false }) 69 | console.log('error resending code...', err) 70 | } 71 | } 72 | 73 | verify = async e => { 74 | e.preventDefault() 75 | const { email, auth_code } = this.state 76 | this.setState({ loading: true }) 77 | try { 78 | await Auth.verifyCurrentUserAttributeSubmit(email, auth_code) 79 | this.setState({ loading: false }) 80 | navigate('/signin') 81 | } catch (err) { 82 | this.setState({ error: err, loading: false }) 83 | console.log('error signing up...', err) 84 | } 85 | } 86 | 87 | confirmSignUp = async e => { 88 | e.preventDefault() 89 | this.setState({ loading: true }) 90 | const { email, auth_code } = this.state 91 | try { 92 | this.setState({ loading: true }) 93 | await Auth.confirmSignUp(email, auth_code) 94 | this.setState({ loading: false }) 95 | navigate('/signin') 96 | } catch (err) { 97 | this.setState({ error: err, loading: false }) 98 | console.log('error confirming signing up...', err) 99 | } 100 | } 101 | 102 | render() { 103 | if (this.state.stage === 0) { 104 | return ( 105 | 106 | 111 | 116 |
117 | 118 | 128 |
129 | 144 |

145 | Have an account? Sign in 146 |

147 |
148 | ) 149 | } 150 | return ( 151 | 152 | 157 | 162 | 177 |

178 | Have an account? Sign in 179 |

180 |
187 |

191 | Lost your code? 192 |

193 | 200 |
201 |
202 | ) 203 | } 204 | } 205 | 206 | export default SignUp 207 | -------------------------------------------------------------------------------- /src/components/Pages/index.js: -------------------------------------------------------------------------------- 1 | import Home from './Home' 2 | import Profile from './Profile' 3 | import IndexPage from './IndexPage' 4 | import Reset from './Reset' 5 | import SignIn from './SignIn' 6 | import SignUp from './SignUp' 7 | 8 | export { Home, Profile, IndexPage, Reset, SignIn, SignUp } 9 | -------------------------------------------------------------------------------- /src/components/Routes/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Redirect } from '@reach/router' 3 | import { AppUser } from '../Auth' 4 | import { Layout } from '../Layout' 5 | 6 | class PrivateRoute extends React.Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = {} 10 | } 11 | componentDidMount() {} 12 | 13 | render() { 14 | const { isLoggedIn } = AppUser 15 | if (!isLoggedIn()) { 16 | return 17 | } 18 | const { component: Component, location, ...rest } = this.props 19 | return ( 20 | 21 | 22 | 23 | ) 24 | } 25 | } 26 | 27 | export default PrivateRoute 28 | -------------------------------------------------------------------------------- /src/components/Routes/PublicRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Redirect } from '@reach/router' 3 | import { AppUser } from '../Auth' 4 | import { Layout } from '../Layout' 5 | 6 | class PublicRoute extends React.Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = {} 10 | } 11 | 12 | componentDidMount() {} 13 | 14 | render() { 15 | const { isLoggedIn } = AppUser 16 | if (isLoggedIn()) { 17 | return 18 | } 19 | const { component: Component, location, ...rest } = this.props 20 | return ( 21 | 22 | 23 | 24 | ) 25 | } 26 | } 27 | 28 | export default PublicRoute 29 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Layout } from '../components/Layout' 3 | 4 | const NotFoundPage = () => ( 5 | 6 |

NOT FOUND

7 |

You just hit a route that doesn't exist... the sadness.

8 |
9 | ) 10 | 11 | export default NotFoundPage 12 | -------------------------------------------------------------------------------- /src/pages/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Router } from '@reach/router' 3 | import { 4 | Profile, 5 | Home, 6 | IndexPage, 7 | Reset, 8 | SignIn, 9 | SignUp, 10 | } from '../components/Pages' 11 | import PrivateRoute from '../components/Routes/PrivateRoute' 12 | import PublicRoute from '../components/Routes/PublicRoute' 13 | import Amplify from 'aws-amplify' 14 | import config from '../aws-exports' 15 | 16 | const App = () => { 17 | Amplify.configure(config) 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ) 28 | } 29 | 30 | export default App 31 | -------------------------------------------------------------------------------- /src/scss/colors.scss: -------------------------------------------------------------------------------- 1 | $white: #fff !default; 2 | $gray-100: #f8f9fa !default; 3 | $gray-200: #e9ecef !default; 4 | $gray-300: #dee2e6 !default; 5 | $gray-400: #ced4da !default; 6 | $gray-500: #adb5bd !default; 7 | $gray-600: #868e96 !default; 8 | $gray-700: #495057 !default; 9 | $gray-800: #343a40 !default; 10 | $gray-900: #212529 !default; 11 | $black: #000 !default; 12 | 13 | $blue: #03a9f4 !default; 14 | $indigo: #3f51b5 !default; 15 | $purple: #673ab7 !default; 16 | $pink: #e91e63 !default; 17 | $red: #f44336 !default; 18 | $orange: #ff5722 !default; 19 | $yellow: #ffab00 !default; 20 | $green: #8bc34a !default; 21 | $teal: #009688 !default; 22 | $cyan: #00bcd4 !default; -------------------------------------------------------------------------------- /src/scss/fonts.scss: -------------------------------------------------------------------------------- 1 | // Font, line-height, and color for body text, headings, and more. 2 | $font-family-sans-serif: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol" !default; 3 | $font-family-monospace: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace !default; 4 | $font-family-base: $font-family-sans-serif !default; 5 | 6 | $font-size-base: 1rem !default; // Assumes the browser default, typically `16px` 7 | $font-size-lg: 1.25rem !default; 8 | $font-size-sm: 0.875rem !default; 9 | $font-size-xs: 0.75rem !default; 10 | 11 | $font-weight-normal: normal !default; 12 | $font-weight-bold: bold !default; 13 | 14 | $font-weight-base: $font-weight-normal !default; 15 | $line-height-base: 1.5 !default; 16 | 17 | $h1-font-size: 1.8rem !default; 18 | $h2-font-size: 1.4rem !default; 19 | $h3-font-size: 1.2rem !default; 20 | $h4-font-size: 1.1rem !default; 21 | $h5-font-size: 1.05rem !default; 22 | $h6-font-size: 1rem !default; 23 | 24 | // $headings-margin-bottom: ($spacer / 2) !default; 25 | $headings-font-family: inherit !default; 26 | $headings-font-weight: 800 !default; 27 | $headings-line-height: 1.5 !default; 28 | $headings-color: inherit !default; 29 | 30 | $display1-size: 6rem !default; 31 | $display2-size: 5.5rem !default; 32 | $display3-size: 4.5rem !default; 33 | $display4-size: 3.5rem !default; -------------------------------------------------------------------------------- /src/scss/styles.scss: -------------------------------------------------------------------------------- 1 | // Colors 2 | // 3 | // Grayscale and brand colors for use across Bootstrap. 4 | 5 | // Start with assigning color names to specific hex values. 6 | // 7 | // Color system 8 | // 9 | @import './colors.scss'; 10 | 11 | // Reassign color vars to semantic color scheme 12 | $theme-colors: ( 13 | primary: $purple, 14 | secondary: $gray-600, 15 | success: $green, 16 | info: $cyan, 17 | warning: $yellow, 18 | danger: $red, 19 | light: $gray-100, 20 | dark: $gray-800, 21 | ) !default; 22 | 23 | // Options 24 | // 25 | // Quickly modify global styling by enabling or disabling optional features. 26 | $enable-rounded: true !default; 27 | $enable-shadows: true !default; 28 | $enable-gradients: false !default; 29 | $enable-transitions: false !default; 30 | $enable-hover-media-query: false !default; 31 | $enable-grid-classes: true !default; 32 | $enable-print-styles: true !default; 33 | 34 | // Grid containers 35 | // N/A 36 | 37 | // Body 38 | // 39 | // Settings for the `` element. 40 | 41 | $body-bg: $white !default; 42 | $body-color: $gray-700 !default; 43 | $inverse-bg: $gray-900 !default; 44 | $inverse-color: $gray-600 !default; 45 | 46 | // Links 47 | // 48 | // Style anchor elements. 49 | $link-decoration: none !default; 50 | $link-hover-decoration: none !default; 51 | 52 | // Fonts 53 | // 54 | @import './fonts.scss'; 55 | 56 | @import 'node_modules/bootstrap/scss/bootstrap.scss'; 57 | 58 | html, 59 | body { 60 | height: 100%; 61 | width: 100%; 62 | } 63 | 64 | * { 65 | -webkit-font-smoothing: antialiased; 66 | -moz-osx-font-smoothing: grayscale; 67 | } 68 | 69 | p { 70 | font-size: 16px; 71 | line-height: 1.5; 72 | margin: 20px 0px; 73 | } 74 | 75 | img { 76 | margin-bottom: 15px; 77 | max-height: 100%; 78 | } 79 | 80 | img:hover { 81 | animation: pulse 0.5s; 82 | } 83 | 84 | .container-login100 { 85 | width: 100%; 86 | min-height: calc(100vh - 56px); 87 | display: -webkit-box; 88 | display: -webkit-flex; 89 | display: -moz-box; 90 | display: -ms-flexbox; 91 | display: flex; 92 | flex-wrap: wrap; 93 | justify-content: center; 94 | align-items: center; 95 | padding: 15px; 96 | 97 | background-position: center; 98 | background-size: cover; 99 | background-repeat: no-repeat; 100 | } 101 | 102 | .wrap-login100 { 103 | width: 390px; 104 | background: #fff; 105 | border-radius: 10px; 106 | position: relative; 107 | } 108 | 109 | .app-content-100 { 110 | background: repeating-linear-gradient( 111 | 45deg, 112 | rgba(103, 58, 183, 0.1), 113 | rgba(103, 58, 183, 0.1) 10px, 114 | rgba(225, 190, 231, 0.1) 10px, 115 | rgba(225, 190, 231, 0.1) 20px 116 | ); 117 | height: calc(100vh - 100px); 118 | width: 100vw; 119 | } 120 | --------------------------------------------------------------------------------