Amplify DevelopmentArticleAWSCognito AuthenticationVue

Let’s start developing… Using Vue and Amplify.

In the last posts we added some background for authentication using Cognito. Let’s start using it in application. So, what language should we choose? JS, of course. What framework/library? Let’s have a think. There are so many supported languages, all modern and sexy. This time, we’ll pick Vue and Vuex – surely the right choice.

As a prerequisite, I have configured environment based on my previous posts but I would like to have a new Cognito and other AWS resources as well.

Let’s jump straight to the point and create a new project and Cognito authentication resources:

npm init

npm i aws-amplify
npm i aws-amplify-vue
npm i @vue/cli
npm i @aws-amplify/cli

npm install --save vuex

npm audit fix

amplify init
amplify add auth

After configuration, it is good to check if all resources are properly configured, and use api push to check if there are no problems with deployment. Some of the resource names are used globally hence your name can conflict with others.

When deploying your project, you may get an error like this:

CREATE_FAILED UserPoolClientLambda                                        AWS::Lambda::Function      Sun Jul 21 2019 22:33:31 GMT+0200 (Central European Daylight Time) The runtime parameter of nodejs6.10 is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejs10.x) while creating or updating functions. (Service: AWSLambdaInternal; Status Code: 400; Error Code: InvalidParameterValueException; Request ID: xxxxx)

More info: https://aws-amplify.github.io/docs/cli/lambda-node-version-update#auth

If you do, you have to change runtime version for your future functions. Edit file in the <project-root>/amplify/backend/auth/<resource-name>/xxxxxxx-cloudformation-template.yml and replace Runtime: nodejs6.10 with Runtime: nodejs8.10 or newer.

So, why the amplify wants to create a new function if we didn’t defined any? It is important to note that, if we choose the authentication for our Lambdas, Amplify is going to create function dedicated to authentication for us.

What does this function look like? We can find it with just one quick look inside AWS Console. At this stage, two lines are of particular interest:

if (event.RequestType == 'Update' || event.RequestType == 'Create')
{   const params = {     ClientId: event.ResourceProperties.clientId,     UserPoolId: event.ResourceProperties.userpoolId   };

Now is the right time to decide which of the backend language to use. In this case JS is ok, considering our next experiments 🙂

To add api and lambda function to test our solution we need to execute command and answer few configuration questions first:

amplify add api

There are some important configuration steps, but Amplify will do all the hard work with configuration for us. First of all, we have to choose which api – REST or GraphQL – we can use and then decide on names, path and target Lambda function name.

Using this command we can select default template and create DynamoDB, using wizard or simple Lambda definition. For this example, I selected only lambda

Next steps are important because we have to decide whether our api will be accessible for all or for authenticated users only. I used authentication to test amplify functionality and check how functions are secured:

And finally to store all resources in the AWS:

amplify push

We have to remember to set correct AWS credentials and profile. It so easy to deploy solution for the wrong account 😉

So, I have created a project and all background stuff needed for this development phase.

Let’s peek into project directory. All created files should be ready for editing and deployment.

And all resource files should be separated in dedicated directory grouped by resource type:

One moment, please. I have to create a project for vue – to have app from template.

Depending on the system, we should use vue create . in project root directory or vue.cmd create . for Windows.

 After a few minutes I have created a project 🙂

So, we have a development environment. Let’s try it and run ‘npm run serve

I have started a server. And I have some new files created by vue-cli. Most importantly, App.vue and main.js were defined as starting point for our application.

Let’s look at the files:

Looking for documentation on Amplify (https://aws-amplify.github.io/docs/js/vue), I had to modify main.js file and add required imports.

import App from "./App.vue";
import Amplify, * as AmplifyModules from "aws-amplify";
import { Auth, Logger } from "aws-amplify";
import { AmplifyPlugin } from "aws-amplify-vue";
import { components } from "aws-amplify-vue";
import { AmplifyEventBus } from "aws-amplify-vue";

import awsconfig from "./aws-exports";
Amplify.configure(awsconfig);

Vue.use(Auth);
//Vue.use(AmplifyPlugin);
Vue.use(AmplifyPlugin, AmplifyModules);

Vue.config.productionTip = false;
new Vue({
 render: h => h(App)

If we use Amplify, we have to remember that the import from ‘./aws-exports’ is crucial, because Amplify prepares the configuration files for the project and puts the credentials into it.

Next, we have to prepare the configuration for authentication. Fortunately, we got all the needed and prepared forms for authentication from a template.

So, we need to prepare the import from Amplify components and use it in the Vue code:

import { components } from "aws-amplify-vue";

How to do it? It’s really quite simple, as all components are ready to use.

<template>
 <div id="app">
   <img alt="Vue logo" src="./assets/logo.png">
   <br/>
   <amplify-sign-in v-bind:signInConfig="signInConfig"></amplify-sign-in>
 </div>
</template>

<script>
import { Auth } from 'aws-amplify';
export default {
 name: "app",
 components: {},
 data() {
   return {
     signInConfig: {
       isSignUpDisplayed: true
     }
   };
 }
};
</script>

As a result of a working application we can see the form ready for login. The login form is using the imports previously configured in the main.js.

So, the Amplify library is built on events and all the changes related to user interactions are distributed to event handler (bus).

AmplifyEventBus.$on("authState", info => {
 console.log(`AUTH: event emitted by an Amplify component: ${info}`);
});

If you need to control some of the authentication state, you can use Vue Router and info value, as in the example below:

AmplifyEventBus.$on("authState", info => {
 logger.debug(`AUTH: event emitted by an Amplify component: ${info}`);
 if (info === "confirmSignIn") {
   router.push("/confirm");
 } else if (info === "signedIn") {
   router.push("/");
 } else if (info === "forgotPassword") {
   router.push("/forgot");
 } else if (info === "confirmSignUp") {
   router.push("/confirmsignup");
 } else if (info === "signedOut") {
   router.push("/login");
 }
});

It is even better to start, using amplify-authenticator – all authentication steps and forms are defined in one component and use standard authentication flow.

<!-- <amplify-sign-in v-bind:signInConfig="signInConfig"></amplify-sign-in> -->
   <amplify-authenticator v-bind:authConfig="authConfig"></amplify-authenticator>

Let’s check what my application looks like. After last steps, I should have a ready to use login form.

First of all, I haven’t created any account yet. Let’s do it now. One click, and…

Wait a minute. I don’t need a Phone Number on the SignUp form. How to change that? Reading the documentation reveals that we can find properties for every authentication sub form:

import { Auth } from "aws-amplify";
export default {
 name: "app",
 components: {},
 data() {
   return {
     authConfig: {
       hideAllDefaults: false,

       signUpConfig: {
         hiddenDefaults: ["phone_number"]
       }
     }
   };
 }
};

And here it is:

Let’s confirm it, using code from registration email:

And finally I can log into the app:

If you want to see all of the flows, look into debug info of the web browser:

All forms are displayed and rendered by one component without the use of Vue Router or any additional source code. What we should do in our simple application is grab Sign In event and store the user information in the application Vue Store. To do so, we need to create store.js:

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
 //strict: true, //TODO: Move to strict mode and refactor code
 state: {
   user: null,
   signedIn: false
 },
 mutations: {
   setUser(state, user) {
     state.user = user;
   },
   setSignedIn(state, signedIn) {
     state.signedIn = signedIn;
   }
 }
});

Now, the main main.js looks like this:

import Vue from "vue";
import App from "./App.vue";
import store from "./store";

import Amplify, * as AmplifyModules from "aws-amplify";
import { Auth, Logger } from "aws-amplify";
import { AmplifyPlugin } from "aws-amplify-vue";
import { components } from "aws-amplify-vue";
import { AmplifyEventBus } from "aws-amplify-vue";

import awsconfig from "./aws-exports";
Amplify.configure(awsconfig);

Vue.use(Auth);
//Vue.use(AmplifyPlugin);
Vue.use(AmplifyPlugin, AmplifyModules);

Amplify.Logger.LOG_LEVEL = "DEBUG";

const logger = new Logger("main");
Auth.currentAuthenticatedUser()
 .then(user => logger.debug(user))
 .catch(err => logger.debug(err));

Vue.config.productionTip = false;

AmplifyEventBus.$on("authState", info => {
 console.log(`AUTH: event emitted by an Amplify component: ${info}`);
 if (info === "signedIn") {
   store.commit("setSignedIn", true);

   Auth.currentAuthenticatedUser()
     .then(data => {
       if (data && data.signInUserSession) {
         store.commit("setUser", data);
       }
     })
     .catch(e => {
       console.log(e);
     });
 }
});

new Vue({
 store,
 render: h => h(App)
}).$mount("#app");

Finally, we can check the status of the logged user in the Vuex Store:

 

Uff.. for the first time it was not clearly getting all steps to prepare configuration but it working.. So most of the boring, to develop every time, steps are included in Amplify library and AWS components. I could use it without wasting time. Combination of Vue, Amplify and Cognito be right for my work and I’m going to use it in prototypes or real projects mixing it with Serverless 🙂

Disclaimer

Bear in mind that I did not try to build a fully secure authorization. This article is only a sandbox – it helps you start with Cognito and provides some basic knowledge about the Authentication process. Every security implementation should be configured carefully and tested fully, because even a small misconfiguration may have a dramatic impact on the application and AWS account security.

So, what did we achieve?

We have prepared environment for future development and project tests, using Cognito service integrated with Vue and Vuex. It is a good starting point for developing other apps or refactoring existing ones to use Cognito from AWS.