Building a Chrome Extension for Quick Account Switching and Management
This article explains how to design and implement a Chrome extension using React, Webpack, and JavaScript to store multiple login accounts, tag them, isolate environments, and provide one‑click login directly from the browser and VS Code, improving efficiency for testers, developers, and product teams.
In many development workflows, repeatedly entering usernames and passwords across different test, pre‑release, and production environments wastes time. The article introduces a Chrome extension that extends the browser's built‑in password manager to enable fast account switching, isolation, and tagging.
Requirements
Support account entry and one‑click login to save input time.
Allow custom tags for each account with full CRUD operations.
Isolate accounts per environment to avoid cross‑contamination.
Provide an easy‑to‑use UI.
Design Overview
The solution is built as a Chrome Extension, which runs in a sandboxed environment and interacts with web pages via content scripts. The extension uses standard web technologies (HTML, JavaScript, CSS) and React for the UI.
Project Structure
.
├── README.md
├── package-lock.json
├── package.json
├── src
│ ├── assets # extension icons
│ ├── contentScript # scripts that run in the page
│ ├── manifest.json # extension manifest
│ └── popup # popup UI
└── webpack.config.jsmanifest.json
{
"name": "Account Saver",
"description": "zcy 账号管理小精灵~",
"version": "1.0",
"manifest_version": 2,
"icons": {
"16": "./assets/icon.png",
"48": "./assets/icon.png",
"96": "./assets/icon.png",
"128": "./assets/icon.png"
},
"browser_action": {
"default_icon": "./assets/icon.png",
"default_title": "账号管理小精灵~",
"default_popup": "/popup.html"
},
"permissions": ["tabs", "activeTab", "storage", "notifications"],
"background": {"persistent": false, "scripts": ["./background.js"]},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["/popupListener.js"],
"run_at": "document_idle"
}]
}webpack.config.js
const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
context: path.resolve(__dirname, './src'),
entry: {
popup: './popup/index.js',
background: './background/index.js',
popupListener: './contentScript/popupListener.js',
},
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/',
filename: '[name].js',
},
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
babelrc: false,
presets: [require.resolve('@babel/preset-react'), require.resolve('@babel/preset-env')],
plugins: ['@babel/plugin-proposal-class-properties'],
cacheDirectory: true,
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({title: 'popup', template: './popup/index.html', inject: true, chunks: ['popup'], filename: 'popup.html'}),
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(['./dist/', './zip/']),
new CopyWebpackPlugin([
{ from: 'assets', to: 'assets' },
{ from: 'manifest.json', to: 'manifest.json', flatten: true },
]),
],
};Core Code
Content Script
The content script injects a React component into pages that contain a username input field on the ZCY domain, creating a floating panel with saved accounts.
const { domain } = document;
const isZcy = domain.indexOf('zcy') !== -1;
const userDom = document.getElementsByName('username')[0];
if (isZcy && userDom) {
const body = document.getElementsByTagName('body')[0];
const panelWrapper = document.createElement('div');
ReactDOM.render(
, panelWrapper);
body.append(panelWrapper);
}One‑Click Login Logic
When a user selects an account, the script fills the username and password fields, dispatches input events, and programmatically clicks the login button.
const usernameDom = document.getElementById('username');
const passwordDom = document.getElementById('password');
const { accountList } = this.state;
const { username, password } = accountList.find(item => item.username === handleUsername);
const evt = new Event('input', { bubbles: true });
usernameDom.value = username;
usernameDom.dispatchEvent(evt);
passwordDom.value = password;
passwordDom.dispatchEvent(evt);
const loginBtn = document.getElementsByClassName('login-btn')[0];
loginBtn.click();Development Helpers
Because hot‑module replacement does not work for extensions, developers must reload the extension manually via chrome://extensions/ or use the “Extensions Reloader” Chrome Web Store tool.
Future Plans
Persist account data to a backend service to avoid loss.
Share account data with a VS Code plugin for local proxy usage.
Add a unified login feature to bridge user identities across browsers.
Introduce business‑group isolation to prevent tag mixing.
Enable multi‑account login for testing scenarios.
The roadmap also includes an E‑R diagram for the data model and references to the official Chrome Extensions documentation.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.