[beta] Learnings from developing a VueJS, Quasar, AWS Amplify (and Cognito) application

Matthias Orgler
8 min readApr 8, 2019
Photo by Tirza van Dijk on Unsplash

This is a beta article. I write and extend this article as I develop an application in VueJS with Quasar and AWS Amplify (including Cognito). I hope, my learnings will already help some of you even without being a fully written article.

Tools used

  • VueJS — JavaScript framework
  • Quasar — Full frontend stack for web, mobile and desktop apps (based on VueJS)
  • AWS Amplify — Cloud infrastructure and services for hosting, CI, authentication … (a little like Google Firebase and Google Cloud)
  • AWS Cognito — User authentication

Why these tools?

You could argue about this for a lifetime. I simply arrived at these tools after trying out a lot of JS frameworks, UI libraries and infrastructure services. They work for me.

VueJS is an awesome JS frontend framework. Easier to learn and master than AngularJS, and also better standards than the fractured ReactJS community. It’s also been gaining in popularity for a long time.

Quasar does not only provide out-of-the-box UI elements in Material Design and iOS/macOS style, but allows to build a web application, Android (using Cordova) and iPhone app as well as desktop applications (using Electron) from the same codebase. Also supports PWA and SSR goodness from the start.

AWS Amplify (update: I don’t recommend this anymore — I use Serverless instead) packages Amazons AWS services into an easy(er) to us suite. AWS service have traditionally been a pain to configure and use — Amplify makes it significantly easier. It feels very much like Google Firebase to me, although Amazon has a much wider and maturer array of services. With Amplify we get Hosting (S3), Authentication (Cognito), API and more — start simple and progressively add the services you need.

AWS Cognito provides user management. Signing in, signing up, reset passwords, MFA, issue API tokens — all that is taken care of in a secure way. If you ever tried to manage giving out, refreshing and revoking JWT tokens for your API, you will appreciate Cognito!

Serverless

As I learn more and more about this topic, I realize, that AWS Amplify is still not ready for productive use. It works well for toy projects and to get to results quickly; it also works well if you can stick with the defaults it provides. But once you encounter real world scenarios (like AppSync with an API backend instead of a DynamoDB or want to use existing Cognito user pools), Amplify is blocking you more than it helps. As an alternative I found the Serverless framework, which has a little steeper learning curve, but allows to do everything in in code (instead of using various tedious AWS web interfaces and hacking internal Amplify config files).

To use multiple environments in Serverless (development, staging, production), use self-referencing variables.

AWS Amplify

Update: I moved away from Amplify after trying to use it in real life for quite some time! See “Serverless” above for my currently recommended alternative.

User pools and identity pools

  • Cannot set “email as login” with Amplify CLI => have to do it manually
  • Create a userpool & identitypool.
  • Create a client app for userpool. Make sure to NOT sign userpool (uncheck box), as this will not work with JS.
  • Link the same client app id to the identitypool. (https://forums.aws.amazon.com/thread.jspa?threadID=250632). For this you have to edit the identitypool on the AWS console and add your client app id under “authentication providers” — you can add multiple app client ids here.
  • Add userpool and identitypool data to aws-exports.js for configuration
  • To be able to read custom attributes from your client, you need to explicitly allow read access to each one in the details of your “app client” in the Cognito console.

Cognito (general)

  • vue components (aws-amplify-vue) do not work. The authentication component for Cognito seems to have some missing dependencies and you cannot sign up. So you have to do it yourself via the Cognito API
  • When creating a new user in a userpool, you can set a temp password and then let the user pick a password. On first login you will receive a “challenge” => you should then show another form where they can enter their password. See https://aws-amplify.github.io/docs/js/authentication#sign-in for how the API works. It’s simpler than it sounds.
  • If a user’s email has not yet been validated (i.e. after you change a user’s email address), the Cognito API will return “User not found” until the email is verified! For the API it seems as though the user was not there, but she is there, just without email_verified set to false. So if you update the user’s email address via admin_set_user_attributes, also include the “email_verified” field and set it to “true”.

Cognito user migration

  • Migration can be done with a lambda function triggered by the Cognito user pool
  • To trigger the migration lambda, you have to use USER_PASSWORD_AUTH . This needs to be set in your client when configuring your Amplify instance AND also in the settings of the client in the Cognito web console. See https://stackoverflow.com/questions/52664612/cognito-user-migration-trigger-not-firing
  • If you want to write custom attributes to the user pool from the user migration lambda function, you have to register that attribute in the user pool first (under “General settings -> Attributes”). Otherwise the migration lambda function will not create a new user.
  • Here is my user migration lambda function:
    https://gist.github.com/morgler/651e5dc48bcfae5680181e1b7bb8d04b
    Note: the new way of returning success/error is to use the “callback” function (not context.succeed or context.fail anymore). callback(“My error”) returns an error, while callback(null, event) returns success.
  • Make sure your backend API does not respond with null values for certain user attributes (like “name”: null). Rather remove the elements that are null from your response (in Ruby simply use compact on the hash).

Use existing Cognito User Pool

Since October 2020 we can finally use existing Cognito User Pools in a new Amplify application. This enables us to progressively connect services to the same user pool, even if the user pool was created outside of Amplify. To connect an existing User Pool to a new Amplify application, simply run amplify import auth on that new Amplify application. You can then select any user pool from your AWS account. Unfortunately it doesn’t seem to be possible to use a User Pool from a different AWS account yet.

Multienv (dev, staging, prod)

CI

Quasar

General

Infinite Scroll

  • Quasar seems to have a bug. Make sure to call done(true) instead of just done(): https://forum.quasar-framework.org/topic/983/solved-endless-scroll-bug
  • => not a bug, but just undocumented: done() => loading another page is done; done(true) => nothing more to load (i.e. last page was loaded). https://github.com/quasarframework/quasar/issues/2863
  • To determine, whether everything has been loaded, look at the links.next URL of the JsonApi repsonse: it will have a link, of more stuff can be loaded or null, if you reached the end of the list. I simply return this boolean value and feed it to infinite scrolls “done” callback.
  • Be aware that the standard offset for starting to load more is pretty easy to trigger (like if you only scrolled through half of the existing list). This ensures that items will already be done loading, when the user reaches the bottom of the list. But this might confuse during development with only few items, as it seems, everything would just be loaded initially ;).

Electron

  • define a separate .env.production file, to build production version of the desktop app. Then define a SERVER_ENV env variable and set it while building the app, so that it uses the correct settings for API url, Cognito userpool and so on. Best is to define a separate yarn script for building each environment, which sets SERVER_ENV and builds the app (e.g. “SERVER_ENV=staging quasar build -m electron”).
  • Add
    webPreferences: {
    webSecurity: false
    }
    to the the electron browser window (electron-main.js) to avoid CORS issues

Cordova

  • The ANDROID_SDK_ROOT (forme ANDROID_HOME) ENV var on MacOS has to point to:
    export ANDROID_SDK_ROOT=”$HOME/Library/Android/Sdk”
    (Quasar docs only have instructions for Linux and Windows!)
  • Also have to add Gradle to the path. It is part of Android Studio, but placed in a different location on a Mac:
    export GRADLE_HOME=”/Applications/Android Studio.app/Contents/gradle/gradle-4.10.1"
    PATH=$PATH:$GRADLE_HOME/bin
    (Notice that the version in the path will be different for updates/different versions!)

General

Debug JS on iPhone

Develop on Android phone

Bonus: Rails Backend

Suppose we have an existing Rails application with user auth powered by Devise. If we want to use this application as an API backend, we need to somehow migrate these users to the new Quasar applications smoothly.

  • To authenticate with Cognito in our Rails app, use a custom Devise strategy. Additionally use a lambda function attached to Cognito to migrate users when the sign in or change their password. See https://c.mirifique.ch/2018/04/09/rails-devise-authenticating-using-aws-cognito-identity/ .
    Note that Aws::CognitoIdentityProvider::Client.new needs {region: aws_region_eg_from_env} set, unless it is provided in ENV[‘AWS_REGION’] already!
    Also note: the “hostname” of https.request must NOT contain the protocol (i.e. use “my-backend.com” instead of “https://my-backend.com”).
  • You need to active USER_PASSWORD_AUTH on the “App Client” in your Cognito user pool.
  • In order to make Cognito work and avoid the Aws::Errors::MissingCredentialsError, you have to set the general AWS credentials (of your IAM user)! Easiest way to do this (see docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/setup-config.html) is to set the two env variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
  • Only if you have been using decise_token_auth before: make sure to set the user.uid in the RegistrationController::create to avoid a DB validation error. http://blog.magmalabs.io/2018/07/03/use-devise-devise_token_auth.html

--

--

Matthias Orgler

Agile Coach, Business Innovator, Software Engineer, Musician