This is a Vue.js v2 app and has been created using Vue CLI 3 using the vue create
command. The Vue project is written in plain ES6 JavaScript.
The Webpack configuration is thankfully handled entirely by the Vue CLI, so there is no external Webpack config file. Babel was not enabled so no polyfilling will occur, therefore resulting app will only run on modern browsers. ESLint was enabled with the 'essential/recommended' configuration. The normal Vue router is used.
Node v8 was used and tested for the building and bundling, but this is not a Node app, Node is only used at build time.
Note. The Vue.js CLI is not required to be installed as a global tool (i.e. the
vue
command) in order to build or serve the app
Main dependencies and libraries used:
- Bootstrap Vue - Main UI framework
- Bootswatch Themes - Cosmo theme used
- Font Awesome - Icons
- Axios - HTTP client
- JSON Server - For mock API
- MSAL.js - Azure AD v2 authentication
- VeeValidate - Data validation
To work locally you will need Node.js v8.9+ installed and NPM (which typically is installed alongside Node).
💬 Note. All commands here require that you are working from the project's
/vue
sub-directory
Run this first! Standard NPM install of project dependencies
npm install
This will start a test server on port 3000 and serve the app, this means you can run/test the client without needing the real frontend Node server. This server should only ever be used for local dev work.
💬 Important! The app will still require a functioning API, and VUE_APP_API_ENDPOINT must be set. So you either need to start the mock API or to point to a real data-api service with the configuration (see below)
npm run serve
To build, bundle and minify the app using Webpack. The resulting output will be placed into the dist
subdirectory (i.e. /vue/dist
from main project root). This output is ready to be served via the Node frontend service.
It's recommended to run build-modern
which uses the Vue CLI newer modern build mode outputting native ES6 modules. By design, no effort has been made in this app to support older browsers.
npm run build-modern
Some notes on the overall structure of the /vue
directory, and some key source files.
The project is a standard Vue CLI template project and has been split logically into a number of Vue components and also some Vue mixins, so you will find:
./src
- All source code./src/main.js
- App entry point, all initialisation done here and mounting of App component./src/App.vue
- Main top level App component, with router outlet and main page structure, navbar etc./src/router.js
- Handles routing, and error and security checks
Other notable files/directories:
./src/components
- All Vue components as .vue files./src/mixins
- Several helper Vue mixins./src/mixins/api.js
- This mixin provides methods for calling the Smilr API./src/auth
- Helper classes used for AAD authentication./public
- Static content and index.html./mock-api
- Fake API server, see below
Configuration of any SPA can be tricky as they run entirely in the user's browser. For Smilr config is done either by Vue CLI's environmental variable support or a custom runtime configuration endpoint. Which is used is dependant which 'mode' the app is running in.
The app stores all settings in a global dictionary object named config
which is exported from main.js
so as to be globally available. When the app starts it populates this config
object as per the two modes described below
This mode is only used when running npm run serve
, i.e. local testing mode. In this mode the .env.development
file is used for configuration and settings. This file is not provided with the repo, however a sample file .env.development.sample
is included which should be copied & renamed. All settings require the VUE_APP_
prefix or they will not be picked up, so API_ENDPOINT
becomes VUE_APP_API_ENDPOINT
This mode is used when the app is properly built and bundled using npm run build
. In this mode the app will try to configure itself at startup using initialization code in main.js
. This initialization process calls a special runtime config API of the Node frontend server, this API provides a simple mechanism for fetching environmental variables from the server. This allows us to reconfigure the app from the sever side, without "baking in" config at build time
This also means when running in production mode the app must be served from the dedicated Smilr Node frontend server, serving from simple static file hosting (e.g. Azure Storage or other hosting) will not work. It also means that configuration is done by setting env variables on the frontend server, much like you would with a classic server-side web app
Variable Name | Description |
---|---|
API_ENDPOINT | Required setting! This points to URL endpoint of the data service API, e.g. https://myapi.azurewebsites.net/api . It must end with /api |
AAD_CLIENT_ID | Optional. The client ID of an app registered in Azure AD, setting this to anything other than blank will "switch security on". Further details are provided in the Security section below. Default: 'blank' |
When running & testing locally it's often inconvenient to have a functioning instance of the data API service, as it also requires MonogDB. To this end a mock API is provided which acts like the real data API and doesn't require MonogDB. The mock API uses JSON Server as a simple REST server
Before starting you will need to create a db.json
file, this should live in the mock-api
sub-directory. Two sample files are provided, so simply copy & rename db.demo.json
or db.empty.json
. The demo version contains some sample demo data. Note. the db.json
file will be written too and modified by the mock API
To start the mock API, just run:
npm run mock-api
The mock API server will start on localhost on port 4000 (just like the real data API), therefore set VUE_APP_API_ENDPOINT=http://localhost:4000/api
in .env.development
By default all authentication & security is disabled, this means users have unrestricted access to admin sections of the UI (presently the 'reports' view & the 'events admin' view). For use in demos, workshops and labs this is mostly likely the desired behavior.
Note. Admin views are denoted with yellow buttons on the navbar, over on the right
Should you want to host Smilr permanently somewhere or to investigate how to secure SPA apps, then authentication & security can be enabled
To secure the Smilr client app we use the Microsoft identity platform (aka 'Azure Active Directory v2') and the OAuth 2.0 Implicit Grant flow. As described in the Azure docs here
Setting the configuration AAD_CLIENT_ID
(or VUE_APP_AAD_CLIENT_ID
when running locally) will switch on security. The value should be the client id of an app registered with Azure AD. Setting this changes the app behavior in two ways
- The Report and Admin components will be protected by a Vue route guard. This guard checks that there is a logged in user and the user is in the list of admins. If there is no logged in user, they are redirected to the Login component.
- If there is a logged in user, all HTTP requests to the data API will include that user's access token as a JWT in the authorization header, as described in the standard OAuth 2.0 Bearer Token scheme.
The Login component uses the MSAL.js library to authenticate using the configured AAD_CLIENT_ID
. As we are a SPA this is done with a popup rather than a redirect.
The user is held as global object exported from main.js
called userProfile
It is checks against this object that determines if a user is logged in. This object holds three things
user
: The returned MSAL Account object, described hereisAdmin
: Deprecated flag no longer usedtoken
: The access token (not id token) returned by MSAL, which is a Base64 encoded JWT string
Note. When
AAD_CLIENT_ID
is unset and security disabled, then a fake shallowuserProfile
is created with a dummy MSAL user object. This results in passing all the user checks, so the app functions as if you are logged in.
Known Limitations. Currently the user session is not persisted locally in any way. This means that refreshing the browser will result in you being logged out. This is considered acceptable for how the app is used.
When creating/registering the app in Azure AD it is very important to set the following options:
- For supported account types, pick "Accounts in any organizational directory and personal Microsoft accounts"
- Enable 'Access tokens' and 'ID tokens'
- Set the correct redirect URI, which should end
/login
- Add a scope to the app via the "Expose and API" section.
- Set the App ID URI to be
api://{app-client-id-guid}
(This might be the default) - Add a scope called: smilr.events
- Set the App ID URI to be
Todo
- UI design
- Use of mixin
- App init logic
- Routing