Repository: wolfeidau/cognito-vue-bootstrap Branch: master Commit: 84801f7fa563 Files: 33 Total size: 34.2 KB Directory structure: gitextract_a2ppqcxr/ ├── .browserslistrc ├── .eslintrc.js ├── .github/ │ └── workflows/ │ └── nodejs.yml ├── .gitignore ├── .nvmrc ├── LICENSE.md ├── Makefile ├── README.md ├── babel.config.js ├── jest.config.js ├── package.json ├── postcss.config.js ├── public/ │ └── index.html ├── src/ │ ├── App.vue │ ├── aws-exports.js │ ├── components/ │ │ ├── Footer.vue │ │ ├── Menu.vue │ │ └── auth/ │ │ └── Alert.vue │ ├── main.js │ ├── pages/ │ │ ├── Dashboard.vue │ │ ├── Home.vue │ │ └── auth/ │ │ ├── ChangePassword.vue │ │ ├── ConfirmPasswordReset.vue │ │ ├── ConfirmSignUp.vue │ │ ├── PasswordReset.vue │ │ ├── SignIn.vue │ │ ├── SignOut.vue │ │ └── SignUp.vue │ ├── router/ │ │ └── index.js │ └── store/ │ ├── index.js │ └── modules/ │ └── auth.js ├── tests/ │ └── unit/ │ └── footer.spec.js └── vue.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .browserslistrc ================================================ > 1% last 2 versions ================================================ FILE: .eslintrc.js ================================================ module.exports = { root: true, env: { node: true }, extends: ["plugin:vue/essential", "@vue/prettier"], rules: { "no-console": process.env.NODE_ENV === "production" ? "error" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off" }, parserOptions: { parser: "babel-eslint" }, overrides: [ { files: [ "**/__tests__/*.{j,t}s?(x)", "**/tests/unit/**/*.spec.{j,t}s?(x)" ], env: { jest: true } } ] }; ================================================ FILE: .github/workflows/nodejs.yml ================================================ name: Node CI on: [push] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [10.x, 12.x] steps: - uses: actions/checkout@v1 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: npm install, build, and test run: | yarn yarn run build --if-present yarn jest env: CI: true ================================================ FILE: .gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw* # Don't commit your aws mobile configuration file .envrc ================================================ FILE: .nvmrc ================================================ 10 ================================================ FILE: LICENSE.md ================================================ Copyright 2018 Mark Wolfe Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Makefile ================================================ ci: clean build deploy clean: rm -rf dist build: yarn build deploy: aws s3 sync dist/ s3://$(S3_HOSTING_BUCKET)/ --delete --acl public-read aws cloudfront create-invalidation --distribution-id $(DISTRIBUTION_ID) --paths '/*' ================================================ FILE: README.md ================================================ # cognito-vue-bootstrap This application illustrates how to use the [Amazon Amplify](https://github.com/aws/aws-amplify) with [vue.js](https://vuejs.org/), it includes signup, signin, signout, recover password and account verification using email or sms codes. It uses [bootstrap-vue](https://bootstrap-vue.js.org/) for layout. # overview This example application currently illustrates the following: * Sign Up for an account * Verify your account by entering verification code which has been sent to you via emailed or SMS * Dashboard which requires authentication to access * Change Password * Recover Password using verification code which has been sent to you via emailed or SMS * Sign In to the application * Sign Out of the application Demo version is located at https://cognito-vue-bootstrapv2.wolfe.id.au/ # Build Setup Before you start have a read over [What is Amazon Cognito?](http://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html) To setup this project you first need to configure an aws mobile project using the following snapshot [CognitoVue](https://console.aws.amazon.com/mobilehub/home#/snapshot/ef9bu3t7nsa8uz). For more information on this process see [Exporting and Importing AWS Mobile Hub Projects](https://docs.aws.amazon.com/aws-mobile/latest/developerguide/project-import-export.html) Once you have imported the project you will have created: * An S3 bucket with Cloudfront for your web application. * A Cognito pool to store your users * An analytics project to capture metrics on your users login / failure ect. Click on the integrate button in your aws mobile project, the download and extract the cloud config zip file, find `aws-exports.js` inside, and replace this file in `src/` directory. I use [yarn](https://yarnpkg.com/) to build and run this project. ``` bash # install dependencies yarn install # serve with hot reload at localhost:8080 yarn serve # build for production with minification yarn build ``` Sync all the files from your `dist` directory up to the S3 hosting bucket within your AWS Mobile project using the following command. ``` aws --region ap-southeast-2 s3 sync dist/ s3://cognitovuebootstrap-hosting-mobilehub-XXXXXXXXXX/ --delete --acl public-read ``` # hosting configuration notes To host a website on a custom URL using AWS mobile I have found some changes to the current setup. Navigating to the buckets and CDN configuration is done via the `Hosting And Streaming` panel in the mobile project UI. * disable website hosting on the hosting S3 bucket * add an Error Page which sends any 404 (not found) errors to `/index.html` in cloudfront * enable redirect http -> https on the origin * configure a route53 domain for your website * configure a AWS Certificate Manager (ACM) certificate for your domain * add an A record in route53 of type `alias` pointing to your Cloudfront distribution, then update the origin domain name to match the FQDN. For a more detailed explanation on how things work, checkout: * [AWS Amplify Documentation](https://aws.github.io/aws-amplify/) * [AWS Amplify Modularization](https://github.com/aws-amplify/amplify-js/wiki/Amplify-modularization) * [docs for vue-cli](https://cli.vuejs.org/) * [docs for vue-router](http://router.vuejs.org/en/) * [docs for vuex](https://vuex.vuejs.org/) # License This project is released under the Apache License, Version 2.0. ================================================ FILE: babel.config.js ================================================ module.exports = { presets: ["@vue/cli-plugin-babel/preset"] }; ================================================ FILE: jest.config.js ================================================ module.exports = { preset: "@vue/cli-plugin-unit-jest" }; ================================================ FILE: package.json ================================================ { "name": "cognito-vue-bootstrap", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "test:unit": "vue-cli-service test:unit", "lint": "vue-cli-service lint" }, "dependencies": { "@aws-amplify/auth": "^1.5.0", "bootstrap-vue": "^2.0.4", "core-js": "^3.3.2", "vue": "^2.6.10", "vue-awesome": "^3.5.4", "vue-resource": "^1.5.1", "vue-router": "^3.1.3", "vuex": "^3.0.1", "vuex-localstorage": "^1.0.0" }, "devDependencies": { "@testing-library/vue": "^4.1.0", "@vue/cli-plugin-babel": "^4.0.0", "@vue/cli-plugin-eslint": "^4.0.0", "@vue/cli-plugin-router": "^4.0.0", "@vue/cli-plugin-unit-jest": "^4.0.0", "@vue/cli-plugin-vuex": "^4.0.0", "@vue/cli-service": "^4.0.0", "@vue/eslint-config-prettier": "^5.0.0", "@vue/test-utils": "1.0.0-beta.29", "babel-eslint": "^10.0.3", "eslint": "^5.16.0", "eslint-plugin-prettier": "^3.1.1", "eslint-plugin-vue": "^5.0.0", "prettier": "^1.18.2", "vue-template-compiler": "^2.6.10" } } ================================================ FILE: postcss.config.js ================================================ module.exports = { plugins: { autoprefixer: {} } }; ================================================ FILE: public/index.html ================================================ cognito-vue-bootstrapv3
================================================ FILE: src/App.vue ================================================ ================================================ FILE: src/aws-exports.js ================================================ const config = { // To get the AWS Credentials, you need to configure // the Auth module with your Cognito Federated Identity Pool Auth: { identityPoolId: process.env.IDENTITY_POOL_ID, region: process.env.AWS_REGION, userPoolId: process.env.USER_POOL_ID, userPoolWebClientId: process.env.USER_POOL_WEB_CLIENT_ID, mandatorySignIn: false, clientMetadata: { app: "cognito-vue-bootstrap" } }, Analytics: { disabled: false, autoSessionRecord: true, AWSPinpoint: { appId: process.env.PINPOINT_APP_ID, region: process.env.AWS_REGION } } }; export default config; ================================================ FILE: src/components/Footer.vue ================================================ ================================================ FILE: src/components/Menu.vue ================================================ ================================================ FILE: src/components/auth/Alert.vue ================================================ ================================================ FILE: src/main.js ================================================ import Vue from "vue"; import BootstrapVue from "bootstrap-vue"; import App from "@/App.vue"; import router from "@/router"; import store from "@/store"; import Auth from "@aws-amplify/auth"; import AuthConfig from "@/aws-exports"; Auth.configure(AuthConfig); Vue.use(BootstrapVue); Vue.config.productionTip = false; new Vue({ router, store, el: "#app", render: h => h(App) }); ================================================ FILE: src/pages/Dashboard.vue ================================================ ================================================ FILE: src/pages/Home.vue ================================================ ================================================ FILE: src/pages/auth/ChangePassword.vue ================================================ ================================================ FILE: src/pages/auth/ConfirmPasswordReset.vue ================================================ ================================================ FILE: src/pages/auth/ConfirmSignUp.vue ================================================ ================================================ FILE: src/pages/auth/PasswordReset.vue ================================================ ================================================ FILE: src/pages/auth/SignIn.vue ================================================ ================================================ FILE: src/pages/auth/SignOut.vue ================================================ ================================================ FILE: src/pages/auth/SignUp.vue ================================================ ================================================ FILE: src/router/index.js ================================================ import Vue from "vue"; import Router from "vue-router"; import Home from "@/pages/Home.vue"; import Dashboard from "@/pages/Dashboard.vue"; import SignIn from "@/pages/auth/SignIn.vue"; import SignUp from "@/pages/auth/SignUp.vue"; import SignOut from "@/pages/auth/SignOut.vue"; import ConfirmSignUp from "@/pages/auth/ConfirmSignUp.vue"; import PasswordReset from "@/pages/auth/PasswordReset.vue"; import ChangePassword from "@/pages/auth/ChangePassword.vue"; import ConfirmPasswordReset from "@/pages/auth/ConfirmPasswordReset.vue"; import store from "@/store"; Vue.use(Router); const routes = [ { path: "/", name: "home", component: Home, meta: { title: "Home", auth: false } }, { path: "/dashboard", name: "dashboard", component: Dashboard, meta: { title: "Dashboard", auth: true } }, { path: "/signIn", name: "signIn", component: SignIn, meta: { title: "Sign In", auth: false } }, { path: "/signOut", name: "signOut", component: SignOut, meta: { title: "Sign Out", auth: true } }, { path: "/signUp", name: "signUp", component: SignUp, meta: { title: "Sign Up", auth: false } }, { path: "/confirmSignUp", name: "confirmSignUp", component: ConfirmSignUp, meta: { title: "Confirm SignUp", auth: false } }, { path: "/changePassword", name: "changePassword", component: ChangePassword, meta: { title: "Change Password", auth: true } }, { path: "/passwordReset", name: "passwordReset", component: PasswordReset, meta: { title: "Password Reset", auth: false } }, { path: "/confirmPasswordReset", name: "confirmPasswordReset", component: ConfirmPasswordReset, meta: { title: "Confirm Password Reset", auth: false } } ]; const router = new Router({ mode: "history", routes }); // this routine will ensure that any pages marked as `auth` in the `meta` section are // protected from access by unauthenticated users. router.beforeEach((to, from, next) => { // Use the page's router title to name the page if (to.meta && to.meta.title) { document.title = to.meta.title; } // is there a meta and auth attribute? if (to.meta && to.meta.auth !== undefined) { // if the page requires auth if (to.meta.auth) { // and we are authenticated? if (store.getters["auth/isAuthenticated"]) { next(); // route normally return; } // otherwise off to the sign in page router.push({ name: "signIn" }); return; } // otherwise are we already authenticated? if (store.getters["auth/isAuthenticated"]) { // yes we are, so off to dashboard router.push({ name: "dashboard" }); return; } next(); // route normally return; } next(); // route normally return; }); export default router; ================================================ FILE: src/store/index.js ================================================ import Vue from "vue"; import Vuex from "vuex"; import createPersist from "vuex-localstorage"; Vue.use(Vuex); // Modules import auth from "./modules/auth"; const debug = process.env.NODE_ENV !== "production"; const store = new Vuex.Store({ modules: { auth }, strict: debug, plugins: [ createPersist({ namespace: "cognito-vue-bootstrap", initialState: {}, // ONE_WEEK expires: 7 * 24 * 60 * 60 * 1e3 }) ] }); export default store; ================================================ FILE: src/store/modules/auth.js ================================================ import Auth from "@aws-amplify/auth"; import Amplify from "@aws-amplify/core"; const Logger = Amplify.Logger; Logger.LOG_LEVEL = "DEBUG"; // to show detailed logs from Amplify library const logger = new Logger("store:auth"); // initial state const state = { user: null, isAuthenticated: false, authenticationStatus: null }; const getters = { authenticatedUser: state => state.user, isAuthenticated: state => state.isAuthenticated, authenticationStatus: state => { return state.authenticationStatus ? state.authenticationStatus : { variant: "secondary" }; }, hasAuthenticationStatus: state => { return !!state.authenticationStatus; } }; const mutations = { setAuthenticationError(state, err) { logger.debug("auth error: {}", err); state.authenticationStatus = { state: "failed", message: err.message, variant: "danger" }; }, clearAuthenticationStatus: state => { state.authenticationStatus = null; }, setUserAuthenticated(state, user) { state.user = user; state.isAuthenticated = true; }, clearAuthentication(state) { state.user = null; state.userId = null; state.isAuthenticated = false; } }; const actions = { clearAuthenticationStatus: context => { context.commit("clearAuthenticationStatus", null); }, signIn: async (context, params) => { logger.debug("signIn for {}", params.username); context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { const user = await Auth.signIn(params.username, params.password); context.commit("setUserAuthenticated", user); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } }, signOut: async context => { try { await Auth.signOut(); } catch (err) { logger.error("error during sign out: {}", err); } context.commit("auth/clearAuthentication", null, { root: true }); }, signUp: async (context, params) => { context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { await Auth.signUp(params); context.commit("auth/clearAuthentication", null, { root: true }); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } }, confirmSignUp: async (context, params) => { logger.debug("confirm signup for {}", params.username); context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { await Auth.confirmSignUp(params.username, params.code); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } }, confirmResend: async (context, params) => { context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { await Auth.resendSignUp(params.username); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } }, passwordReset: async (context, params) => { context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { await Auth.forgotPassword(params.username); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } }, confirmPasswordReset: async (context, params) => { context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { await Auth.forgotPasswordSubmit( params.username, params.code, params.password ); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } }, passwordResetResend: async (context, params) => { context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { await Auth.passwordResetResend(params.username); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } }, passwordChange: async (context, params) => { logger.debug("password change for {}", context.state.user.username); context.commit("auth/clearAuthenticationStatus", null, { root: true }); try { const user = await Auth.currentAuthenticatedUser(); await Auth.changePassword( user, params.currentPassword, params.newPassword ); } catch (err) { context.commit("auth/setAuthenticationError", err, { root: true }); } } }; export default { namespaced: true, state, getters, actions, mutations }; ================================================ FILE: tests/unit/footer.spec.js ================================================ import { render } from '@testing-library/vue' import Footer from "@/components/Footer.vue"; describe("Footer.vue", () => { it("renders props.msg when passed", () => { const { getByText } = render(Footer) getByText('© 2018 Company, Inc.') }); }); ================================================ FILE: vue.config.js ================================================ // vue.config.js module.exports = { transpileDependencies: [/\bvue-awesome\b/] };