This is a guest post from CodePen community member Nour Soud.
Hackers (the bad kind) have a lot of tools and techniques to try to guess our passwords. Setting up a secure, long and complicated password is the least we can do if we want to protect ourselves and our data.
We should also have a unique password for each app/service we use to make sure we don’t lose everything if one of the accounts got compromised.
In my case, whenever I try to generate a secure password, I always either:
Use 1password’s password generator:
Pros | Cons |
---|---|
Nice design | You have to have 1password installed which is paid and not everyone can afford it. |
Simple Password generator with the main and basic options | You have to login to 1password before generating the password |
Or, Google “good password generator”, pick the top listed and use it:
Pros | Cons |
---|---|
No need for prior installation. | Not user-friendly |
Too many options that people rarely care about |
None of the above options felt perfect for me, and I had the feeling that I can combine the pros of both in one simple web app.
The Implementation
I’ve always loved experimenting with Vue.js, and I just launched a todo-list manager using Vue.js a month before, so I thought it would be the best approach to take.
As always, I jumped to CodePen and started building the app. It took me a while to get the design right, I changed it a lot and played with it a lot.
Designing the range-sliders took me some time to do, but with the plenty of contributions on CodePen, I gathered inspiration from other people’s Pens (like “CSS Range Slider” by Sean Stopnik) and one of my own, and came up with my sliders.
See the Pen The Password Genie – Vue.js by Nour Soud (@nourabusoud) on CodePen.
Going Live
I first published the password generator on CodePen so people can engage with and learn from my experiment, and also perhaps fork it and make a better version of it.
I wanted the password generator to be really used by people, so I thought of hosting it on GitHub pages would be the perfect and simplest way to do it, and in the future if it had traffic I might get it it’s own domain name for better accessibility!
I then shared it on Twitter and I got lots of great feedback from awesome designers and developers! The amount of joy that gave me was unbelievable!
Some people even requested a browser extension. I found this to be a chance here to learn something new as I have to admit that I never even looked on how these extensions are built and always thought they would be so hard to make!
The Chrome Extension
I did a bit of research on how to make Chrome extensions. It looked easy at first, but I had some decisions to make.
Usually if you want to make a chrome extension what you have to do is:
- Create a
popup.html
page and put your HTML there. There should be no inline scripts at all. - Create a
popup.js
file to put you JavaScript code in. - Create a
manifest.json
file to describe your plugin.
Example:
{
"manifest_version": 2,
"name": "The Password Genie",
"description": "Secure and instant password generator",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": []
}
You’ll need to create the icon listed there in the manifest as well.
In my project, I’ve used Vue by directly including it via a CDN in the <script>
tag, this way allows you to create your Vue app in the same HTML page, example:
<body>
<div id="app">
{{ message }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello World!'
}
})
</script>
</body>
This wasn’t possible for building the chrome extension, as you can’t include inline scripts due to Content-Security-Policy (CSP). I had to find another solution. Either do it with plain JavaScript or create a Vue project using the runtime-only version. I actually did both, but ended up going live with the Vue option.
Building the Chrome Extension
I did some research and found this really good step-by-step article. I’ll explain it in details below as I’ve modified the process a little:
1: Install the Chrome Extension generator. You can find the steps in the readme.
2: Install additional packages:
npm install --save webpack webpack-stream babel-loader vue vue-loader css-loader vue-template-compiler babel-preset-env
3: In gulpfile.babel.js
, at the top of the file, add these imports:
import webpackStream from 'webpack-stream';
import webpack from 'webpack'
4: Replace the following tasks in gulpfile.babel.js
:
Babel task
gulp.task('babel', () => {
return gulp.src('app/scripts.babel')
.pipe(webpackStream(require('./webpack.config.js'), webpack)
.on('error', function (err) {
console.log(err);
this.emit('end');
}))
.pipe(gulp.dest('app/scripts/'))
});
Watch task to watch for .vue
extension changes
gulp.task('watch', ['lint', 'babel'], () => {
$.livereload.listen();
gulp.watch([
'app/*.html',
'app/scripts/**/*.js',
'app/scripts/**/*.vue',
'app/images/**/*',
'app/styles/**/*',
'app/_locales/**/*.json'
]).on('change', $.livereload.reload);
gulp.watch(['app/scripts.babel/**/*.js', 'app/scripts.babel/**/*.vue'], ['lint', 'babel']);
gulp.watch('bower.json', ['wiredep']);
});
gulp.task('size', () => {
return gulp.src('dist/**/*').pipe($.size({title: 'build', gzip: true}));
});
And modify the lint function
gulp.task('lint', lint('app/scripts.babel/**/*.js', {
env: {
es6: true
},
parserOptions: {
sourceType: 'module'
}
}));
5: Create webpack.config.js
in the project root folder and add the following code to it:
module.exports = {
context: __dirname + '/app/scripts.babel/',
entry: {
chromereload: './chromereload.js',
popup: './popup.js',
},
output: { filename: '[name].js' },
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader'
}, {
test: /\.vue$/,
loader: 'vue-loader'
}]
}
}
6: Adding in my Vue application:
I created my main Vue component in: app/scripts.babel/popup/Popup.vue
, then I copied the code from my Pen with some changes:
- Component template should only contain exactly one root element, so everything in my case should be under the
<section>
tag - Since I’m creating a component here I should use
export default
to declare it.
<template>
<section class="wrapper">
<!-- my code went here
.........
-->
</section>
</template>
<script>
export default {
data() {
//....
}
}
</script>
Then I created my app instant in my popup.html
file
<div id="app"></div>
I used the code below in my popup.js file to render my new component in my application and creating a new Vue instance.
import Vue from 'vue';
import Popup from './popup/Popup.vue';
new Vue({
el: '#app',
render: c => c(Popup)
});
Testing the Extension
First I had to build the application first using:
gulp build
I used the /dist/
folder as my source for the Chrome extension.
- In Chrome I went to:
chrome://extensions/
- Enabled Developer mode
- Loaded the app from the
/dist/
folder as an unpacked extension
Adjusting the Design
The password generator worked perfectly, but looked very big for an extension. So, I had to tweak my design to make it more extension-like. I had to adjust the sizes, and clean it up by removing some unnecessary elements to make it as compact and user-friendly as possible.
Voilà, Now I have a working good-looking password generator that’s only one click away!