From 0f33f70ee490a5b4836b9b72329262fad6d3313c Mon Sep 17 00:00:00 2001 From: dtomlinson Date: Sun, 15 Mar 2020 02:12:16 +0000 Subject: [PATCH] adding latest --- .gitignore | 15 + amplify/.config/project-config.json | 17 + ...orial1c1c40c25-cloudformation-template.yml | 390 ++ .../parameters.json | 58 + amplify/backend/backend-config.json | 9 + amplify/team-provider-info.json | 20 + notes.md | 168 +- package.json | 3 + src/App.vue | 92 +- src/main.js | 19 +- src/router/index.js | 51 +- src/utils/auth.js | 97 + src/views/Home.vue | 18 - src/views/SignIn.vue | 68 + src/views/SignUp.vue | 11 +- src/views/SignUpConfirm.vue | 5 +- tutorial.md | 30 + yarn.lock | 3641 ++++++++++++++++- 18 files changed, 4495 insertions(+), 217 deletions(-) create mode 100644 amplify/.config/project-config.json create mode 100644 amplify/backend/auth/mediumawstutorial1c1c40c25/mediumawstutorial1c1c40c25-cloudformation-template.yml create mode 100644 amplify/backend/auth/mediumawstutorial1c1c40c25/parameters.json create mode 100644 amplify/backend/backend-config.json create mode 100644 amplify/team-provider-info.json create mode 100644 src/utils/auth.js delete mode 100644 src/views/Home.vue create mode 100644 src/views/SignIn.vue create mode 100644 tutorial.md diff --git a/.gitignore b/.gitignore index a0dddc6..1a4ce61 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,18 @@ yarn-error.log* *.njsproj *.sln *.sw? + +#amplify +amplify/\#current-cloud-backend +amplify/.config/local-* +amplify/mock-data +amplify/backend/amplify-meta.json +amplify/backend/awscloudformation +build/ +dist/ +node_modules/ +aws-exports.js +awsconfiguration.json +amplifyconfiguration.json +amplify-gradle-config.json +amplifyxc.config \ No newline at end of file diff --git a/amplify/.config/project-config.json b/amplify/.config/project-config.json new file mode 100644 index 0000000..cf5371b --- /dev/null +++ b/amplify/.config/project-config.json @@ -0,0 +1,17 @@ +{ + "projectName": "mediumawstutorial1", + "version": "3.0", + "frontend": "javascript", + "javascript": { + "framework": "vue", + "config": { + "SourceDir": "src", + "DistributionDir": "dist", + "BuildCommand": "npm run-script build", + "StartCommand": "npm run-script serve" + } + }, + "providers": [ + "awscloudformation" + ] +} \ No newline at end of file diff --git a/amplify/backend/auth/mediumawstutorial1c1c40c25/mediumawstutorial1c1c40c25-cloudformation-template.yml b/amplify/backend/auth/mediumawstutorial1c1c40c25/mediumawstutorial1c1c40c25-cloudformation-template.yml new file mode 100644 index 0000000..bbd42dc --- /dev/null +++ b/amplify/backend/auth/mediumawstutorial1c1c40c25/mediumawstutorial1c1c40c25-cloudformation-template.yml @@ -0,0 +1,390 @@ +AWSTemplateFormatVersion: 2010-09-09 + +Parameters: + env: + Type: String + authRoleArn: + Type: String + unauthRoleArn: + Type: String + + + + + identityPoolName: + Type: String + + allowUnauthenticatedIdentities: + Type: String + + resourceNameTruncated: + Type: String + + userPoolName: + Type: String + + autoVerifiedAttributes: + Type: CommaDelimitedList + + mfaConfiguration: + Type: String + + mfaTypes: + Type: CommaDelimitedList + + smsAuthenticationMessage: + Type: String + + smsVerificationMessage: + Type: String + + emailVerificationSubject: + Type: String + + emailVerificationMessage: + Type: String + + defaultPasswordPolicy: + Type: String + + passwordPolicyMinLength: + Type: Number + + passwordPolicyCharacters: + Type: CommaDelimitedList + + requiredAttributes: + Type: CommaDelimitedList + + userpoolClientGenerateSecret: + Type: String + + userpoolClientRefreshTokenValidity: + Type: Number + + userpoolClientWriteAttributes: + Type: CommaDelimitedList + + userpoolClientReadAttributes: + Type: CommaDelimitedList + + userpoolClientLambdaRole: + Type: String + + userpoolClientSetAttributes: + Type: String + + resourceName: + Type: String + + authSelections: + Type: String + + useDefault: + Type: String + + usernameAttributes: + Type: CommaDelimitedList + + triggers: + Type: String + + userPoolGroupList: + Type: CommaDelimitedList + + parentStack: + Type: String + + permissions: + Type: CommaDelimitedList + + dependsOn: + Type: CommaDelimitedList + +Conditions: + ShouldNotCreateEnvResources: !Equals [ !Ref env, NONE ] + +Resources: + + + # BEGIN SNS ROLE RESOURCE + SNSRole: + # Created to allow the UserPool SMS Config to publish via the Simple Notification Service during MFA Process + Type: AWS::IAM::Role + Properties: + RoleName: !If [ShouldNotCreateEnvResources, 'mediumc1c40c25_sns-role', !Join ['',[ 'sns', !Select [3, !Split ['-', !Ref 'AWS::StackName']], '-', !Ref env]]] + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "" + Effect: "Allow" + Principal: + Service: "cognito-idp.amazonaws.com" + Action: + - "sts:AssumeRole" + Condition: + StringEquals: + sts:ExternalId: mediumc1c40c25_role_external_id + Policies: + - + PolicyName: mediumc1c40c25-sns-policy + PolicyDocument: + Version: "2012-10-17" + Statement: + - + Effect: "Allow" + Action: + - "sns:Publish" + Resource: "*" + # BEGIN USER POOL RESOURCES + UserPool: + # Created upon user selection + # Depends on SNS Role for Arn if MFA is enabled + Type: AWS::Cognito::UserPool + UpdateReplacePolicy: Retain + Properties: + UserPoolName: !If [ShouldNotCreateEnvResources, !Ref userPoolName, !Join ['',[!Ref userPoolName, '-', !Ref env]]] + + Schema: + + - + Name: email + Required: true + Mutable: true + + + + + AutoVerifiedAttributes: !Ref autoVerifiedAttributes + + + EmailVerificationMessage: !Ref emailVerificationMessage + EmailVerificationSubject: !Ref emailVerificationSubject + + Policies: + PasswordPolicy: + MinimumLength: !Ref passwordPolicyMinLength + RequireLowercase: false + RequireNumbers: false + RequireSymbols: false + RequireUppercase: false + + UsernameAttributes: !Ref usernameAttributes + + MfaConfiguration: !Ref mfaConfiguration + SmsVerificationMessage: !Ref smsVerificationMessage + SmsConfiguration: + SnsCallerArn: !GetAtt SNSRole.Arn + ExternalId: mediumc1c40c25_role_external_id + + + + + + + + + + + + # Updating lambda role with permissions to Cognito + + + UserPoolClientWeb: + # Created provide application access to user pool + # Depends on UserPool for ID reference + Type: "AWS::Cognito::UserPoolClient" + Properties: + ClientName: mediumc1c40c25_app_clientWeb + + RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity + UserPoolId: !Ref UserPool + DependsOn: UserPool + UserPoolClient: + # Created provide application access to user pool + # Depends on UserPool for ID reference + Type: "AWS::Cognito::UserPoolClient" + Properties: + ClientName: mediumc1c40c25_app_client + + GenerateSecret: !Ref userpoolClientGenerateSecret + RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity + UserPoolId: !Ref UserPool + DependsOn: UserPool + # BEGIN USER POOL LAMBDA RESOURCES + UserPoolClientRole: + # Created to execute Lambda which gets userpool app client config values + Type: 'AWS::IAM::Role' + Properties: + RoleName: !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',['upClientLambdaRole', !Select [3, !Split ['-', !Ref 'AWS::StackName']], '-', !Ref env]]] + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - 'sts:AssumeRole' + DependsOn: UserPoolClient + UserPoolClientLambda: + # Lambda which gets userpool app client config values + # Depends on UserPool for id + # Depends on UserPoolClientRole for role ARN + Type: 'AWS::Lambda::Function' + Properties: + Code: + ZipFile: !Join + - |+ + - - 'const response = require(''cfn-response'');' + - 'const aws = require(''aws-sdk'');' + - 'const identity = new aws.CognitoIdentityServiceProvider();' + - 'exports.handler = (event, context, callback) => {' + - ' if (event.RequestType == ''Delete'') { ' + - ' response.send(event, context, response.SUCCESS, {})' + - ' }' + - ' if (event.RequestType == ''Update'' || event.RequestType == ''Create'') {' + - ' const params = {' + - ' ClientId: event.ResourceProperties.clientId,' + - ' UserPoolId: event.ResourceProperties.userpoolId' + - ' };' + - ' identity.describeUserPoolClient(params).promise()' + - ' .then((res) => {' + - ' response.send(event, context, response.SUCCESS, {''appSecret'': res.UserPoolClient.ClientSecret});' + - ' })' + - ' .catch((err) => {' + - ' response.send(event, context, response.FAILED, {err});' + - ' });' + - ' }' + - '};' + Handler: index.handler + Runtime: nodejs10.x + Timeout: '300' + Role: !GetAtt + - UserPoolClientRole + - Arn + DependsOn: UserPoolClientRole + UserPoolClientLambdaPolicy: + # Sets userpool policy for the role that executes the Userpool Client Lambda + # Depends on UserPool for Arn + # Marked as depending on UserPoolClientRole for easier to understand CFN sequencing + Type: 'AWS::IAM::Policy' + Properties: + PolicyName: mediumc1c40c25_userpoolclient_lambda_iam_policy + Roles: + - !Ref UserPoolClientRole + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - 'cognito-idp:DescribeUserPoolClient' + Resource: !GetAtt UserPool.Arn + DependsOn: UserPoolClientLambda + UserPoolClientLogPolicy: + # Sets log policy for the role that executes the Userpool Client Lambda + # Depends on UserPool for Arn + # Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing + Type: 'AWS::IAM::Policy' + Properties: + PolicyName: mediumc1c40c25_userpoolclient_lambda_log_policy + Roles: + - !Ref UserPoolClientRole + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 'logs:CreateLogGroup' + - 'logs:CreateLogStream' + - 'logs:PutLogEvents' + Resource: !Sub + - arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:* + - { region: !Ref "AWS::Region", account: !Ref "AWS::AccountId", lambda: !Ref UserPoolClientLambda} + DependsOn: UserPoolClientLambdaPolicy + UserPoolClientInputs: + # Values passed to Userpool client Lambda + # Depends on UserPool for Id + # Depends on UserPoolClient for Id + # Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing + Type: 'Custom::LambdaCallout' + Properties: + ServiceToken: !GetAtt UserPoolClientLambda.Arn + clientId: !Ref UserPoolClient + userpoolId: !Ref UserPool + DependsOn: UserPoolClientLogPolicy + + + + + + + + # BEGIN IDENTITY POOL RESOURCES + + + IdentityPool: + # Always created + Type: AWS::Cognito::IdentityPool + Properties: + IdentityPoolName: !If [ShouldNotCreateEnvResources, 'mediumawstutorial1c1c40c25_identitypool_c1c40c25', !Join ['',['mediumawstutorial1c1c40c25_identitypool_c1c40c25', '__', !Ref env]]] + + CognitoIdentityProviders: + - ClientId: !Ref UserPoolClient + ProviderName: !Sub + - cognito-idp.${region}.amazonaws.com/${client} + - { region: !Ref "AWS::Region", client: !Ref UserPool} + - ClientId: !Ref UserPoolClientWeb + ProviderName: !Sub + - cognito-idp.${region}.amazonaws.com/${client} + - { region: !Ref "AWS::Region", client: !Ref UserPool} + + AllowUnauthenticatedIdentities: !Ref allowUnauthenticatedIdentities + + + DependsOn: UserPoolClientInputs + + + IdentityPoolRoleMap: + # Created to map Auth and Unauth roles to the identity pool + # Depends on Identity Pool for ID ref + Type: AWS::Cognito::IdentityPoolRoleAttachment + Properties: + IdentityPoolId: !Ref IdentityPool + Roles: + unauthenticated: !Ref unauthRoleArn + authenticated: !Ref authRoleArn + DependsOn: IdentityPool + + +Outputs : + + IdentityPoolId: + Value: !Ref 'IdentityPool' + Description: Id for the identity pool + IdentityPoolName: + Value: !GetAtt IdentityPool.Name + + + + + UserPoolId: + Value: !Ref 'UserPool' + Description: Id for the user pool + UserPoolName: + Value: !Ref userPoolName + AppClientIDWeb: + Value: !Ref 'UserPoolClientWeb' + Description: The user pool app client id for web + AppClientID: + Value: !Ref 'UserPoolClient' + Description: The user pool app client id + AppClientSecret: + Value: !GetAtt UserPoolClientInputs.appSecret + + + + + + + diff --git a/amplify/backend/auth/mediumawstutorial1c1c40c25/parameters.json b/amplify/backend/auth/mediumawstutorial1c1c40c25/parameters.json new file mode 100644 index 0000000..f07b177 --- /dev/null +++ b/amplify/backend/auth/mediumawstutorial1c1c40c25/parameters.json @@ -0,0 +1,58 @@ +{ + "identityPoolName": "mediumawstutorial1c1c40c25_identitypool_c1c40c25", + "allowUnauthenticatedIdentities": false, + "resourceNameTruncated": "mediumc1c40c25", + "userPoolName": "mediumawstutorial1c1c40c25_userpool_c1c40c25", + "autoVerifiedAttributes": [ + "email" + ], + "mfaConfiguration": "OFF", + "mfaTypes": [ + "SMS Text Message" + ], + "smsAuthenticationMessage": "Your authentication code is {####}", + "smsVerificationMessage": "Your verification code is {####}", + "emailVerificationSubject": "Your verification code", + "emailVerificationMessage": "Your verification code is {####}", + "defaultPasswordPolicy": false, + "passwordPolicyMinLength": 8, + "passwordPolicyCharacters": [], + "requiredAttributes": [ + "email" + ], + "userpoolClientGenerateSecret": true, + "userpoolClientRefreshTokenValidity": 30, + "userpoolClientWriteAttributes": [ + "email" + ], + "userpoolClientReadAttributes": [ + "email" + ], + "userpoolClientLambdaRole": "mediumc1c40c25_userpoolclient_lambda_role", + "userpoolClientSetAttributes": false, + "resourceName": "mediumawstutorial1c1c40c25", + "authSelections": "identityPoolAndUserPool", + "authRoleArn": { + "Fn::GetAtt": [ + "AuthRole", + "Arn" + ] + }, + "unauthRoleArn": { + "Fn::GetAtt": [ + "UnauthRole", + "Arn" + ] + }, + "useDefault": "default", + "usernameAttributes": [ + "email" + ], + "triggers": "{}", + "userPoolGroupList": [], + "parentStack": { + "Ref": "AWS::StackId" + }, + "permissions": [], + "dependsOn": [] +} \ No newline at end of file diff --git a/amplify/backend/backend-config.json b/amplify/backend/backend-config.json new file mode 100644 index 0000000..5828264 --- /dev/null +++ b/amplify/backend/backend-config.json @@ -0,0 +1,9 @@ +{ + "auth": { + "mediumawstutorial1c1c40c25": { + "service": "Cognito", + "providerPlugin": "awscloudformation", + "dependsOn": [] + } + } +} \ No newline at end of file diff --git a/amplify/team-provider-info.json b/amplify/team-provider-info.json new file mode 100644 index 0000000..2c7927b --- /dev/null +++ b/amplify/team-provider-info.json @@ -0,0 +1,20 @@ +{ + "tutorial": { + "awscloudformation": { + "AuthRoleName": "amplify-mediumawstutorial1-tutorial-231922-authRole", + "UnauthRoleArn": "arn:aws:iam::745437999005:role/amplify-mediumawstutorial1-tutorial-231922-unauthRole", + "AuthRoleArn": "arn:aws:iam::745437999005:role/amplify-mediumawstutorial1-tutorial-231922-authRole", + "Region": "eu-west-1", + "DeploymentBucketName": "amplify-mediumawstutorial1-tutorial-231922-deployment", + "UnauthRoleName": "amplify-mediumawstutorial1-tutorial-231922-unauthRole", + "StackName": "amplify-mediumawstutorial1-tutorial-231922", + "StackId": "arn:aws:cloudformation:eu-west-1:745437999005:stack/amplify-mediumawstutorial1-tutorial-231922/3e47fe20-664a-11ea-a762-0a736ea8438a", + "AmplifyAppId": "d2uwjppecsv6xz" + }, + "categories": { + "auth": { + "mediumawstutorial1c1c40c25": {} + } + } + } +} \ No newline at end of file diff --git a/notes.md b/notes.md index 5f3ba5e..78c4789 100644 --- a/notes.md +++ b/notes.md @@ -122,16 +122,26 @@ https://stackoverflow.com/a/44350932 With computed you are creating getters/setters. You do this by writing the function in the `computed` directive. Here we have written a computed property for `emailRules()` - we are using this function to check input on a form is correct. +Computed properties don't take any arguments - but you can access the `data` scope through `$this.` if you need access to the data in the scope. + +They are better than using watchers/methods if you don't need the extra functionality. A good example is https://michaelnthiessen.com/most-important-feature-vue/ + The main difference between `computed` and a `method` is that `computed` will cache automatically. It will only update, if the value updates. Everytime you call it it will use its cache. Expensive operations can be done this way. +They are properties (like in Python). Computed properties will update whenever one of the values needed to make it is also updated. Whenever you want to filter or transform your data, you can use a Computed property. + +E.g if you have a list of names, and you want just those names that begin with 'b', you can write it as a computed property that returns this list. The stack overflow answer above demonstrates this. + Remember: anything in `data` you can access in the ` + + diff --git a/src/main.js b/src/main.js index 6b9c034..2fd8a40 100644 --- a/src/main.js +++ b/src/main.js @@ -1,14 +1,19 @@ -import Vue from 'vue' -import App from './App.vue' -import router from './router' -import store from './store' -import vuetify from './plugins/vuetify'; +import Vue from "vue"; +import App from "./App.vue"; +import router from "./router"; +import store from "./store"; +import vuetify from "./plugins/vuetify"; +import Amplify from "aws-amplify"; +import awsconfig from "./aws-exports"; -Vue.config.productionTip = false +Amplify.configure(awsconfig); +Vue.use(Amplify); + +Vue.config.productionTip = false; new Vue({ router, store, vuetify, render: h => h(App) -}).$mount('#app') +}).$mount("#app"); diff --git a/src/router/index.js b/src/router/index.js index d36779e..ac15bef 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,29 +1,48 @@ -import Vue from 'vue' -import VueRouter from 'vue-router' -import Home from '../views/Home.vue' +import Vue from "vue"; +import VueRouter from "vue-router"; +// import Home from "../views/Home.vue"; -Vue.use(VueRouter) +Vue.use(VueRouter); const routes = [ + // { + // path: "/", + // name: "Home", + // component: Home + // }, { - path: '/', - name: 'Home', - component: Home - }, - { - path: '/about', - name: 'About', + path: "/about", + name: "About", // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. - component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') + component: () => + import(/* webpackChunkName: "about" */ "../views/About.vue") + }, + { + path: "/signUp", + name: "signUp", + component: () => + import(/* webpackChunkName: "signup" */ "../views/SignUp.vue") + }, + { + path: "/signUpConfirm", + name: "signUpConfirm", + component: () => + import(/* webpackChunkName: "confirm" */ "../views/SignUpConfirm.vue") + }, + { + path: "/signIn", + name: "signIn", + component: () => + import(/* webpackChunkName: "signin" */ "../views/SignIn.vue") } -] +]; const router = new VueRouter({ - mode: 'history', + mode: "history", base: process.env.BASE_URL, routes -}) +}); -export default router +export default router; diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 0000000..87b5534 --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,97 @@ +import { Auth } from "aws-amplify"; +import { AmplifyEventBus } from "aws-amplify-vue"; + +function getUser() { + return Auth.currentAuthenticatedUser() + .then(user => { + if (user && user.signInUserSession) { + return user; + } else { + return null; + } + }) + .catch(err => { + console.log(err); + }); +} + +function signUp(username, password) { + return Auth.signUp({ + username, + password, + attributes: { + email: username + } + }) + .then(data => { + AmplifyEventBus.$emit("localUser", data.user); + if (data.userConfirmed === false) { + AmplifyEventBus.$emit("authState", "confirmSignUp"); + } else { + AmplifyEventBus.$emit("authState", "signIn"); + } + }) + .catch(err => { + console.log(err); + }); +} + +function confirmSignUp(username, code) { + return Auth.confirmSignIn(username, code) + .then(data => { + AmplifyEventBus.$emit("authState", "signIn"); + return data; // successful sign up + }) + .catch(err => { + console.log(err); + throw err; + }); +} + +function resendSignUp(username) { + return Auth.resendSignUp(username) + .then(() => { + return "Success"; + }) + .catch(err => { + console.log(err); + return err; + }); +} + +async function signIn(username, password) { + try { + const user = await Auth.signIn(username, password); + if (user) { + AmplifyEventBus.$emit("authState", "signedIn"); + } + } catch (err) { + if (err.code === "UserNotConfirmedException") { + // will happen if the user did not finish the confirmation step + // will need to resend the code and then confirm the user + } else if (err.code === "PasswordResetRequiredException") { + // happens when the password is reset in the cognito console + // will need to reset the password (forgotten) + } else if (err.code === "NotAuthorizedException") { + // will happen when the password is incorrect + } else if (err.code === "UserNotFoundException") { + // will happen if the user/email does not exist in the cognito pool + } else { + console.log(err); + } + } +} + +function signOut() { + return Auth.signOut() + .then(data => { + AmplifyEventBus.$emit("authState", "signedOut"); + return data; + }) + .catch(err => { + console.log(err); + return err; + }); +} + +export {getUser, signUp, confirmSignUp, resendSignUp, signIn, signOut} diff --git a/src/views/Home.vue b/src/views/Home.vue deleted file mode 100644 index 8bd6c57..0000000 --- a/src/views/Home.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/src/views/SignIn.vue b/src/views/SignIn.vue new file mode 100644 index 0000000..66da6f3 --- /dev/null +++ b/src/views/SignIn.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/src/views/SignUp.vue b/src/views/SignUp.vue index 7ecec33..a9aefd9 100644 --- a/src/views/SignUp.vue +++ b/src/views/SignUp.vue @@ -11,7 +11,7 @@