├── LICENSE
├── README.md
└── vue-adal-example
├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── README.md
├── build
├── build.js
├── check-versions.js
├── logo.png
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
├── webpack.prod.conf.js
└── webpack.test.conf.js
├── config
├── dev.env.js
├── index.js
└── prod.env.js
├── index.html
├── package.json
├── src
├── App.vue
├── assets
│ └── logo.png
├── authentication
│ └── index.js
├── components
│ └── HelloWorld.vue
├── main.js
└── router
│ └── index.js
└── static
└── .gitkeep
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Matt Ankerson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-adal
2 | A sample Vue.js application showcasing usage of [ADAL JS](https://github.com/AzureAD/azure-activedirectory-library-for-js).
3 |
4 | The purpose of this example is to demonstrate usage of ADAL JS from the `adal-angular` module for managing authentication with Azure AD in the context of a typical Vue.js single page application.
5 |
6 | ### Dependencies
7 | ``` JavaScript
8 | "dependencies": {
9 | "adal-angular": "^1.0.15",
10 | "vue": "^2.5.2",
11 | "vue-router": "^3.0.1"
12 | },
13 | ```
14 |
15 | ### Build and run this sample:
16 | ``` Bash
17 | cd './vue-adal-example'
18 |
19 | # install dependencies
20 | npm install
21 |
22 | # serve with hot reload at localhost:8080
23 | npm run dev
24 | ```
25 |
26 | ## ADAL wrapper module:
27 | `vue-adal-example/src/authentication/index.js`
28 |
29 | ADAL requires some configuration to integrate with an Azure Active Directory application:
30 | ``` JavaScript
31 | const config = {
32 | tenant: 'your aad tenant',
33 | clientId: 'your aad application client id',
34 | redirectUri: 'base uri for this application',
35 | cacheLocation: 'localStorage'
36 | };
37 | ```
38 | Initialise ADAL at page load - before the app is created.
39 |
40 | Usage: `initialize().then(_ => {/* Create vue app */});`
41 | ``` JavaScript
42 | initialize() {
43 | this.authenticationContext = new AuthenticationContext(config);
44 |
45 | return new Promise((resolve, reject) => {
46 | if (this.authenticationContext.isCallback(window.location.hash) || window.self !== window.top) {
47 | // redirect to the location specified in the url params.
48 | this.authenticationContext.handleWindowCallback();
49 | }
50 | else {
51 | // try pull the user out of local storage
52 | let user = this.authenticationContext.getCachedUser();
53 |
54 | if (user) {
55 | resolve();
56 | }
57 | else {
58 | // no user at all - go sign in.
59 | this.signIn();
60 | }
61 | }
62 | });
63 | },
64 | ```
65 | You'll usually need to get an access token for some resource (usually an API you want your SPA to consume). The resource identifier should be associated with another Azure Active Directory application which represents the resource you're requesting:
66 | ``` JavaScript
67 | acquireToken() {
68 | return new Promise((resolve, reject) => {
69 | this.authenticationContext.acquireToken('resource id', (error, token) => {
70 | if (error || !token) {
71 | return reject(error);
72 | } else {
73 | return resolve(token);
74 | }
75 | });
76 | });
77 | },
78 | ```
79 | In the event that something goes wrong (perhaps the user has not accepted the permissions granted to access the requested resource) - an interactive authentication request can be invoked with this function:
80 | ``` JavaScript
81 | acquireTokenRedirect() {
82 | this.authenticationContext.acquireTokenRedirect('resource id');
83 | },
84 | ```
85 | ADAL can be queried to determine whether the user has been properly authenticated:
86 | ``` JavaScript
87 | isAuthenticated() {
88 | // getCachedToken will only return a valid, non-expired token.
89 | if (this.authenticationContext.getCachedToken(config.clientId)) { return true; }
90 | return false;
91 | },
92 | ```
93 | It can be useful to get access to the current users JWT token/profile. This will contain user information, assigned groups, app roles and other handy things.
94 |
95 | ``` JavaScript
96 | getUserProfile() {
97 | return this.authenticationContext.getCachedUser().profile;
98 | },
99 | ```
100 | Wrapper functions to invoke log in and log out actions:
101 | ``` JavaScript
102 | signIn() {
103 | this.authenticationContext.login();
104 | },
105 | signOut() {
106 | this.authenticationContext.logOut();
107 | }
108 | ```
109 |
110 | ## In the app's entry point:
111 | `vue-adal-example/src/main.js`
112 | ``` JavaScript
113 | import Vue from 'vue'
114 | import App from './App'
115 | import router from './router'
116 | import authentication from './authentication'
117 |
118 | authentication.initialize().then(_ => {
119 | new Vue({
120 | el: '#app',
121 | router,
122 | template: '',
123 | components: { App }
124 | });
125 | });
126 |
127 | ```
128 |
129 | Optional - save the user profile in a state store (like Vuex):
130 | ``` JavaScript
131 | let profile = getUserProfile();
132 | store.commit('SET_USER_PROFILE', { profile });
133 | ```
134 |
135 | ## Vue Router
136 | `vue-adal-example/src/router/index.js`
137 |
138 | * Enable history mode (disable usage of hashes (#) in the url).
139 | * Include a meta field to facilitate authentication checking.
140 |
141 | [Vue Router documentation](https://router.vuejs.org/en/)
142 |
143 | ``` JavaScript
144 | const router = new Router({
145 | mode: 'history', // Use history mode to stop vue-router from using a hash in the url.
146 | // This is necessary to allow adal's redirect to work.
147 | routes: [
148 | {
149 | path: '/',
150 | name: 'HelloWorld',
151 | component: HelloWorld,
152 | meta: {
153 | requiresAuthentication: true
154 | }
155 | }
156 | ]
157 | })
158 | ```
159 | Check for the route's meta field `requiresAuthentication`:
160 | ``` JavaScript
161 | // Global route guard
162 | router.beforeEach((to, from, next) => {
163 | if (to.matched.some(record => record.meta.requiresAuthentication)) {
164 | // this route requires auth, check if logged in
165 | if (authentication.isAuthenticated()) {
166 | // only proceed if authenticated.
167 | next();
168 | } else {
169 | authentication.signIn();
170 | }
171 | } else {
172 | next();
173 | }
174 | });
175 | ```
176 |
177 | ## In your Vue components
178 | Use functions from ADAL in your Vue components to drive the view. i.e. - `v-if="isAuthenticated"`
179 | ``` JavaScript
180 | computed: {
181 | isAuthenticated() {
182 | return authentication.isAuthenticated();
183 | }
184 | },
185 | methods: {
186 | logOut() {
187 | authentication.signOut();
188 | }
189 | }
190 | ```
191 |
192 | ## Requesting an access token for a protected resource
193 | ADAL's `acquireToken` function can be used to get a valid token. The following example uses [Vue Resource](https://github.com/pagekit/vue-resource) to set up a http interceptor (not included in sample code).
194 | ``` JavaScript
195 | Vue.http.interceptors.push(function (request, next) {
196 | auth.acquireToken().then(token => {
197 | // Set default request headers for every request
198 | request.headers.set('Content-Type', 'application/json');
199 | request.headers.set('Ocp-Apim-Subscription-Key', 'api key');
200 | request.headers.set('Authorization', 'Bearer ' + token)
201 | // continue to next interceptor
202 | next();
203 | });
204 | });
205 | ```
206 |
--------------------------------------------------------------------------------
/vue-adal-example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false
5 | }],
6 | "stage-2"
7 | ],
8 | "plugins": ["transform-runtime"],
9 | "env": {
10 | "test": {
11 | "presets": ["env", "stage-2"] }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/vue-adal-example/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/vue-adal-example/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 |
--------------------------------------------------------------------------------
/vue-adal-example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
13 | extends: 'standard',
14 | // required to lint *.vue files
15 | plugins: [
16 | 'html'
17 | ],
18 | // add your custom rules here
19 | 'rules': {
20 | // allow paren-less arrow functions
21 | 'arrow-parens': 0,
22 | // allow async-await
23 | 'generator-star-spacing': 0,
24 | // allow debugger during development
25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
26 | // semi colons
27 | "semi": 0,
28 | // Function paranthesis spacing
29 | "space-before-function-paren": 0
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/vue-adal-example/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 |
--------------------------------------------------------------------------------
/vue-adal-example/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserslist" field in package.json
6 | "autoprefixer": {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/vue-adal-example/README.md:
--------------------------------------------------------------------------------
1 | # vue-adal-example
2 |
3 | > A standard issue Vue.js project - using ADAL js for authentication
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 |
17 | # build for production and view the bundle analyzer report
18 | npm run build --report
19 | ```
20 |
21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
22 |
--------------------------------------------------------------------------------
/vue-adal-example/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, function (err, stats) {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false,
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/vue-adal-example/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 | function exec (cmd) {
7 | return require('child_process').execSync(cmd).toString().trim()
8 | }
9 |
10 | const versionRequirements = [
11 | {
12 | name: 'node',
13 | currentVersion: semver.clean(process.version),
14 | versionRequirement: packageConfig.engines.node
15 | }
16 | ]
17 |
18 | if (shell.which('npm')) {
19 | versionRequirements.push({
20 | name: 'npm',
21 | currentVersion: exec('npm --version'),
22 | versionRequirement: packageConfig.engines.npm
23 | })
24 | }
25 |
26 | module.exports = function () {
27 | const warnings = []
28 | for (let i = 0; i < versionRequirements.length; i++) {
29 | const mod = versionRequirements[i]
30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
31 | warnings.push(mod.name + ': ' +
32 | chalk.red(mod.currentVersion) + ' should be ' +
33 | chalk.green(mod.versionRequirement)
34 | )
35 | }
36 | }
37 |
38 | if (warnings.length) {
39 | console.log('')
40 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
41 | console.log()
42 | for (let i = 0; i < warnings.length; i++) {
43 | const warning = warnings[i]
44 | console.log(' ' + warning)
45 | }
46 | console.log()
47 | process.exit(1)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/vue-adal-example/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matt-ankerson/vue-adal/0a883ff87a50b3a98ea6bf74ee5aaa7b90ff7a5e/vue-adal-example/build/logo.png
--------------------------------------------------------------------------------
/vue-adal-example/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const pkg = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 | return path.posix.join(assetsSubDirectory, _path)
12 | }
13 |
14 | exports.cssLoaders = function (options) {
15 | options = options || {}
16 |
17 | const cssLoader = {
18 | loader: 'css-loader',
19 | options: {
20 | minimize: process.env.NODE_ENV === 'production',
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | var postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 | if (loader) {
36 | loaders.push({
37 | loader: loader + '-loader',
38 | options: Object.assign({}, loaderOptions, {
39 | sourceMap: options.sourceMap
40 | })
41 | })
42 | }
43 |
44 | // Extract CSS when that option is specified
45 | // (which is the case during production build)
46 | if (options.extract) {
47 | return ExtractTextPlugin.extract({
48 | use: loaders,
49 | fallback: 'vue-style-loader'
50 | })
51 | } else {
52 | return ['vue-style-loader'].concat(loaders)
53 | }
54 | }
55 |
56 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
57 | return {
58 | css: generateLoaders(),
59 | postcss: generateLoaders(),
60 | less: generateLoaders('less'),
61 | sass: generateLoaders('sass', { indentedSyntax: true }),
62 | scss: generateLoaders('sass'),
63 | stylus: generateLoaders('stylus'),
64 | styl: generateLoaders('stylus')
65 | }
66 | }
67 |
68 | // Generate loaders for standalone style files (outside of .vue)
69 | exports.styleLoaders = function (options) {
70 | const output = []
71 | const loaders = exports.cssLoaders(options)
72 | for (const extension in loaders) {
73 | const loader = loaders[extension]
74 | output.push({
75 | test: new RegExp('\\.' + extension + '$'),
76 | use: loader
77 | })
78 | }
79 | return output
80 | }
81 |
82 | exports.createNotifierCallback = function () {
83 | const notifier = require('node-notifier')
84 |
85 | return (severity, errors) => {
86 | if (severity !== 'error') {
87 | return
88 | }
89 | const error = errors[0]
90 |
91 | const filename = error.file.split('!').pop()
92 | notifier.notify({
93 | title: pkg.name,
94 | message: severity + ': ' + error.name,
95 | subtitle: filename || '',
96 | icon: path.join(__dirname, 'logo.png')
97 | })
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/vue-adal-example/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 |
10 | module.exports = {
11 | loaders: utils.cssLoaders({
12 | sourceMap: sourceMapEnabled,
13 | extract: isProduction
14 | }),
15 | cssSourceMap: sourceMapEnabled,
16 | transformToRequire: {
17 | video: 'src',
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/vue-adal-example/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 | module.exports = {
12 | context: path.resolve(__dirname, '../'),
13 | entry: {
14 | app: './src/main.js'
15 | },
16 | output: {
17 | path: config.build.assetsRoot,
18 | filename: '[name].js',
19 | publicPath: process.env.NODE_ENV === 'production'
20 | ? config.build.assetsPublicPath
21 | : config.dev.assetsPublicPath
22 | },
23 | resolve: {
24 | extensions: ['.js', '.vue', '.json'],
25 | alias: {
26 | 'vue$': 'vue/dist/vue.esm.js',
27 | '@': resolve('src'),
28 | }
29 | },
30 | module: {
31 | rules: [
32 | ...(config.dev.useEslint? [{
33 | test: /\.(js|vue)$/,
34 | loader: 'eslint-loader',
35 | enforce: 'pre',
36 | include: [resolve('src'), resolve('test')],
37 | options: {
38 | formatter: require('eslint-friendly-formatter'),
39 | emitWarning: !config.dev.showEslintErrorsInOverlay
40 | }
41 | }] : []),
42 | {
43 | test: /\.vue$/,
44 | loader: 'vue-loader',
45 | options: vueLoaderConfig
46 | },
47 | {
48 | test: /\.js$/,
49 | loader: 'babel-loader',
50 | include: [resolve('src'), resolve('test')]
51 | },
52 | {
53 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
54 | loader: 'url-loader',
55 | options: {
56 | limit: 10000,
57 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
58 | }
59 | },
60 | {
61 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
62 | loader: 'url-loader',
63 | options: {
64 | limit: 10000,
65 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
66 | }
67 | },
68 | {
69 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
70 | loader: 'url-loader',
71 | options: {
72 | limit: 10000,
73 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
74 | }
75 | }
76 | ]
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/vue-adal-example/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const baseWebpackConfig = require('./webpack.base.conf')
7 | const HtmlWebpackPlugin = require('html-webpack-plugin')
8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
9 | const portfinder = require('portfinder')
10 |
11 | const devWebpackConfig = merge(baseWebpackConfig, {
12 | module: {
13 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
14 | },
15 | // cheap-module-eval-source-map is faster for development
16 | devtool: config.dev.devtool,
17 |
18 | // these devServer options should be customized in /config/index.js
19 | devServer: {
20 | historyApiFallback: true,
21 | hot: true,
22 | host: process.env.HOST || config.dev.host,
23 | port: process.env.PORT || config.dev.port,
24 | open: config.dev.autoOpenBrowser,
25 | overlay: config.dev.errorOverlay ? {
26 | warnings: false,
27 | errors: true,
28 | } : false,
29 | publicPath: config.dev.assetsPublicPath,
30 | proxy: config.dev.proxyTable,
31 | quiet: true, // necessary for FriendlyErrorsPlugin
32 | watchOptions: {
33 | poll: config.dev.poll,
34 | }
35 | },
36 | plugins: [
37 | new webpack.DefinePlugin({
38 | 'process.env': require('../config/dev.env')
39 | }),
40 | new webpack.HotModuleReplacementPlugin(),
41 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
42 | new webpack.NoEmitOnErrorsPlugin(),
43 | // https://github.com/ampedandwired/html-webpack-plugin
44 | new HtmlWebpackPlugin({
45 | filename: 'index.html',
46 | template: 'index.html',
47 | inject: true
48 | }),
49 | new FriendlyErrorsPlugin()
50 | ]
51 | })
52 |
53 | module.exports = new Promise((resolve, reject) => {
54 | portfinder.basePort = process.env.PORT || config.dev.port
55 | portfinder.getPort((err, port) => {
56 | if (err) {
57 | reject(err)
58 | } else {
59 | // publish the new Port, necessary for e2e tests
60 | process.env.PORT = port
61 | // add port to devServer config
62 | devWebpackConfig.devServer.port = port
63 |
64 | // Add FriendlyErrorsPlugin
65 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
66 | compilationSuccessInfo: {
67 | messages: [`Your application is running here: http://${config.dev.host}:${port}`],
68 | },
69 | onErrors: config.dev.notifyOnErrors
70 | ? utils.createNotifierCallback()
71 | : undefined
72 | }))
73 |
74 | resolve(devWebpackConfig)
75 | }
76 | })
77 | })
78 |
--------------------------------------------------------------------------------
/vue-adal-example/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
12 |
13 | const env = require('../config/prod.env')
14 |
15 | const webpackConfig = merge(baseWebpackConfig, {
16 | module: {
17 | rules: utils.styleLoaders({
18 | sourceMap: config.build.productionSourceMap,
19 | extract: true,
20 | usePostCSS: true
21 | })
22 | },
23 | devtool: config.build.productionSourceMap ? config.build.devtool : false,
24 | output: {
25 | path: config.build.assetsRoot,
26 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
27 | chunkFilename: utils.assetsPath('js/[name].[chunkhash].js')
28 | },
29 | plugins: [
30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
31 | new webpack.DefinePlugin({
32 | 'process.env': env
33 | }),
34 | // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify
35 | new webpack.optimize.UglifyJsPlugin({
36 | compress: {
37 | warnings: false
38 | },
39 | sourceMap: config.build.productionSourceMap,
40 | parallel: true
41 | }),
42 | // extract css into its own file
43 | new ExtractTextPlugin({
44 | filename: utils.assetsPath('css/[name].[contenthash].css'),
45 | // set the following option to `true` if you want to extract CSS from
46 | // codesplit chunks into this main css file as well.
47 | // This will result in *all* of your app's CSS being loaded upfront.
48 | allChunks: false,
49 | }),
50 | // Compress extracted CSS. We are using this plugin so that possible
51 | // duplicated CSS from different components can be deduped.
52 | new OptimizeCSSPlugin({
53 | cssProcessorOptions: config.build.productionSourceMap
54 | ? { safe: true, map: { inline: false } }
55 | : { safe: true }
56 | }),
57 | // generate dist index.html with correct asset hash for caching.
58 | // you can customize output by editing /index.html
59 | // see https://github.com/ampedandwired/html-webpack-plugin
60 | new HtmlWebpackPlugin({
61 | filename: config.build.index,
62 | template: 'index.html',
63 | inject: true,
64 | minify: {
65 | removeComments: true,
66 | collapseWhitespace: true,
67 | removeAttributeQuotes: true
68 | // more options:
69 | // https://github.com/kangax/html-minifier#options-quick-reference
70 | },
71 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
72 | chunksSortMode: 'dependency'
73 | }),
74 | // keep module.id stable when vender modules does not change
75 | new webpack.HashedModuleIdsPlugin(),
76 | // enable scope hoisting
77 | new webpack.optimize.ModuleConcatenationPlugin(),
78 | // split vendor js into its own file
79 | new webpack.optimize.CommonsChunkPlugin({
80 | name: 'vendor',
81 | minChunks: function (module) {
82 | // any required modules inside node_modules are extracted to vendor
83 | return (
84 | module.resource &&
85 | /\.js$/.test(module.resource) &&
86 | module.resource.indexOf(
87 | path.join(__dirname, '../node_modules')
88 | ) === 0
89 | )
90 | }
91 | }),
92 | // extract webpack runtime and module manifest to its own file in order to
93 | // prevent vendor hash from being updated whenever app bundle is updated
94 | new webpack.optimize.CommonsChunkPlugin({
95 | name: 'manifest',
96 | minChunks: Infinity,
97 | }),
98 | // This instance extracts shared chunks from code splitted chunks and bundles them
99 | // in a separate chunk, similar to the vendor chunk
100 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
101 | new webpack.optimize.CommonsChunkPlugin({
102 | name: 'app',
103 | async: 'vendor-async',
104 | children: true,
105 | minChunks: 3,
106 | }),
107 |
108 | // copy custom static assets
109 | new CopyWebpackPlugin([
110 | {
111 | from: path.resolve(__dirname, '../static'),
112 | to: config.build.assetsSubDirectory,
113 | ignore: ['.*']
114 | }
115 | ])
116 | ]
117 | })
118 |
119 | if (config.build.productionGzip) {
120 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
121 |
122 | webpackConfig.plugins.push(
123 | new CompressionWebpackPlugin({
124 | asset: '[path].gz[query]',
125 | algorithm: 'gzip',
126 | test: new RegExp(
127 | '\\.(' +
128 | config.build.productionGzipExtensions.join('|') +
129 | ')$'
130 | ),
131 | threshold: 10240,
132 | minRatio: 0.8
133 | })
134 | )
135 | }
136 |
137 | if (config.build.bundleAnalyzerReport) {
138 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
139 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
140 | }
141 |
142 | module.exports = webpackConfig
143 |
--------------------------------------------------------------------------------
/vue-adal-example/build/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // This is the webpack config used for unit tests.
3 |
4 | const utils = require('./utils')
5 | const webpack = require('webpack')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 |
9 | const webpackConfig = merge(baseWebpackConfig, {
10 | // use inline sourcemap for karma-sourcemap-loader
11 | module: {
12 | rules: utils.styleLoaders()
13 | },
14 | devtool: '#inline-source-map',
15 | resolveLoader: {
16 | alias: {
17 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
18 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
19 | 'scss-loader': 'sass-loader'
20 | }
21 | },
22 | plugins: [
23 | new webpack.DefinePlugin({
24 | 'process.env': require('../config/test.env')
25 | })
26 | ]
27 | })
28 |
29 | // no need for app entry during tests
30 | delete webpackConfig.entry
31 |
32 | module.exports = webpackConfig
33 |
--------------------------------------------------------------------------------
/vue-adal-example/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/vue-adal-example/config/index.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict'
3 | // Template version: 1.2.0
4 | // see http://vuejs-templates.github.io/webpack for documentation.
5 |
6 | const path = require('path')
7 |
8 | module.exports = {
9 | dev: {
10 |
11 | // Paths
12 | assetsSubDirectory: 'static',
13 | assetsPublicPath: '/',
14 | proxyTable: {},
15 |
16 | // Various Dev Server settings
17 | host: 'localhost', // can be overwritten by process.env.HOST
18 | port: 8080, // can be overwritten by process.env.HOST, if port is in use, a free one will be determined
19 | autoOpenBrowser: false,
20 | errorOverlay: true,
21 | notifyOnErrors: true,
22 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
23 |
24 | // Use Eslint Loader?
25 | // If true, your code will be linted during bundling and
26 | // linting errors and warings will be shown in the console.
27 | useEslint: true,
28 | // If true, eslint errors and warings will also be shown in the error overlay
29 | // in the browser.
30 | showEslintErrorsInOverlay: false,
31 |
32 | /**
33 | * Source Maps
34 | */
35 |
36 | // https://webpack.js.org/configuration/devtool/#development
37 | devtool: 'eval-source-map',
38 |
39 | // If you have problems debugging vue-files in devtools,
40 | // set this to false - it *may* help
41 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
42 | cacheBusting: true,
43 |
44 | // CSS Sourcemaps off by default because relative paths are "buggy"
45 | // with this option, according to the CSS-Loader README
46 | // (https://github.com/webpack/css-loader#sourcemaps)
47 | // In our experience, they generally work as expected,
48 | // just be aware of this issue when enabling this option.
49 | cssSourceMap: false,
50 | },
51 |
52 | build: {
53 | // Template for index.html
54 | index: path.resolve(__dirname, '../dist/index.html'),
55 |
56 | // Paths
57 | assetsRoot: path.resolve(__dirname, '../dist'),
58 | assetsSubDirectory: 'static',
59 | assetsPublicPath: '/',
60 |
61 | /**
62 | * SourceMap
63 | */
64 | productionSourceMap: true,
65 | // https://webpack.js.org/configuration/devtool/#production
66 | devtool: '#source-map',
67 |
68 | // Gzip off by default as many popular static hosts such as
69 | // Surge or Netlify already gzip all static assets for you.
70 | // Before setting to `true`, make sure to:
71 | // npm install --save-dev compression-webpack-plugin
72 | productionGzip: false,
73 | productionGzipExtensions: ['js', 'css'],
74 |
75 | // Run the build command with an extra argument to
76 | // View the bundle analyzer report after build finishes:
77 | // `npm run build --report`
78 | // Set to `true` or `false` to always turn it on or off
79 | bundleAnalyzerReport: process.env.npm_config_report
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/vue-adal-example/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/vue-adal-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-adal-example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/vue-adal-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-adal-example",
3 | "version": "1.0.0",
4 | "description": "A standard issue Vue.js project - using ADAL js for authentication",
5 | "author": "Matt Ankerson",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "lint": "eslint --ext .js,.vue src",
11 | "build": "node build/build.js"
12 | },
13 | "dependencies": {
14 | "adal-angular": "^1.0.15",
15 | "vue": "^2.5.2",
16 | "vue-router": "^3.0.1"
17 | },
18 | "devDependencies": {
19 | "autoprefixer": "^7.1.2",
20 | "babel-core": "^6.22.1",
21 | "babel-eslint": "^7.1.1",
22 | "babel-loader": "^7.1.1",
23 | "babel-plugin-transform-runtime": "^6.22.0",
24 | "babel-preset-env": "^1.3.2",
25 | "babel-preset-stage-2": "^6.22.0",
26 | "babel-register": "^6.22.0",
27 | "chalk": "^2.0.1",
28 | "copy-webpack-plugin": "^4.0.1",
29 | "css-loader": "^0.28.0",
30 | "eslint": "^3.19.0",
31 | "eslint-friendly-formatter": "^3.0.0",
32 | "eslint-loader": "^1.7.1",
33 | "eslint-plugin-html": "^3.0.0",
34 | "eslint-config-standard": "^10.2.1",
35 | "eslint-plugin-promise": "^3.4.0",
36 | "eslint-plugin-standard": "^3.0.1",
37 | "eslint-plugin-import": "^2.7.0",
38 | "eslint-plugin-node": "^5.2.0",
39 | "eventsource-polyfill": "^0.9.6",
40 | "express": "^4.14.1",
41 | "extract-text-webpack-plugin": "^3.0.0",
42 | "file-loader": "^1.1.4",
43 | "friendly-errors-webpack-plugin": "^1.6.1",
44 | "html-webpack-plugin": "^2.30.1",
45 | "webpack-bundle-analyzer": "^2.9.0",
46 | "node-notifier": "^5.1.2",
47 | "semver": "^5.3.0",
48 | "shelljs": "^0.7.6",
49 | "optimize-css-assets-webpack-plugin": "^3.2.0",
50 | "ora": "^1.2.0",
51 | "rimraf": "^2.6.0",
52 | "url-loader": "^0.5.8",
53 | "vue-loader": "^13.3.0",
54 | "vue-style-loader": "^3.0.1",
55 | "vue-template-compiler": "^2.5.2",
56 | "portfinder": "^1.0.13",
57 | "webpack": "^3.6.0",
58 | "webpack-dev-server": "^2.9.1",
59 | "webpack-merge": "^4.1.0"
60 | },
61 | "engines": {
62 | "node": ">= 4.0.0",
63 | "npm": ">= 3.0.0"
64 | },
65 | "browserslist": [
66 | "> 1%",
67 | "last 2 versions",
68 | "not ie <= 8"
69 | ]
70 | }
71 |
--------------------------------------------------------------------------------
/vue-adal-example/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
36 |
--------------------------------------------------------------------------------
/vue-adal-example/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matt-ankerson/vue-adal/0a883ff87a50b3a98ea6bf74ee5aaa7b90ff7a5e/vue-adal-example/src/assets/logo.png
--------------------------------------------------------------------------------
/vue-adal-example/src/authentication/index.js:
--------------------------------------------------------------------------------
1 | import AuthenticationContext from 'adal-angular/lib/adal.js'
2 |
3 | const config = {
4 | tenant: 'your aad tenant',
5 | clientId: 'your aad application client id',
6 | redirectUri: 'base uri for this application',
7 | cacheLocation: 'localStorage'
8 | };
9 |
10 | export default {
11 | authenticationContext: null,
12 | /**
13 | * @return {Promise}
14 | */
15 | initialize() {
16 | this.authenticationContext = new AuthenticationContext(config);
17 |
18 | return new Promise((resolve, reject) => {
19 | if (this.authenticationContext.isCallback(window.location.hash) || window.self !== window.top) {
20 | // redirect to the location specified in the url params.
21 | this.authenticationContext.handleWindowCallback();
22 | }
23 | else {
24 | // try pull the user out of local storage
25 | let user = this.authenticationContext.getCachedUser();
26 |
27 | if (user) {
28 | resolve();
29 | }
30 | else {
31 | // no user at all - go sign in.
32 | this.signIn();
33 | }
34 | }
35 | });
36 | },
37 | /**
38 | * @return {Promise.} A promise that resolves to an ADAL token for resource access
39 | */
40 | acquireToken() {
41 | return new Promise((resolve, reject) => {
42 | this.authenticationContext.acquireToken('', (error, token) => {
43 | if (error || !token) {
44 | return reject(error);
45 | } else {
46 | return resolve(token);
47 | }
48 | });
49 | });
50 | },
51 | /**
52 | * Issue an interactive authentication request for the current user and the api resource.
53 | */
54 | acquireTokenRedirect() {
55 | this.authenticationContext.acquireTokenRedirect('');
56 | },
57 | /**
58 | * @return {Boolean} Indicates if there is a valid, non-expired access token present in localStorage.
59 | */
60 | isAuthenticated() {
61 | // getCachedToken will only return a valid, non-expired token.
62 | if (this.authenticationContext.getCachedToken(config.clientId)) { return true; }
63 | return false;
64 | },
65 | /**
66 | * @return An ADAL user profile object.
67 | */
68 | getUserProfile() {
69 | return this.authenticationContext.getCachedUser().profile;
70 | },
71 | signIn() {
72 | this.authenticationContext.login();
73 | },
74 | signOut() {
75 | this.authenticationContext.logOut();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/vue-adal-example/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
Essential Links
5 |
13 |
Ecosystem
14 |
20 |
21 |
22 |
23 |
33 |
34 |
35 |
51 |
--------------------------------------------------------------------------------
/vue-adal-example/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import router from './router'
6 | import authentication from './authentication'
7 |
8 | Vue.config.productionTip = false
9 |
10 | // Init adal authentication - then create Vue app.
11 | authentication.initialize().then(_ => {
12 | /* eslint-disable no-new */
13 | new Vue({
14 | el: '#app',
15 | router,
16 | template: '',
17 | components: { App }
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/vue-adal-example/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import HelloWorld from '@/components/HelloWorld'
4 | import authentication from '../authentication'
5 |
6 | Vue.use(Router)
7 |
8 | const router = new Router({
9 | mode: 'history',
10 | routes: [
11 | {
12 | path: '/',
13 | name: 'HelloWorld',
14 | component: HelloWorld,
15 | meta: {
16 | requiresAuthentication: true
17 | }
18 | }
19 | ]
20 | })
21 |
22 | // Global route guard
23 | router.beforeEach((to, from, next) => {
24 | if (to.matched.some(record => record.meta.requiresAuthentication)) {
25 | // this route requires auth, check if logged in
26 | if (authentication.isAuthenticated()) {
27 | // only proceed if authenticated.
28 | next();
29 | } else {
30 | authentication.signIn();
31 | }
32 | } else {
33 | next();
34 | }
35 | });
36 |
37 | export default router;
38 |
--------------------------------------------------------------------------------
/vue-adal-example/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matt-ankerson/vue-adal/0a883ff87a50b3a98ea6bf74ee5aaa7b90ff7a5e/vue-adal-example/static/.gitkeep
--------------------------------------------------------------------------------