
Account Security
- 66 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Protect customer accounts with brute-force lockouts, MFA/TOTP, secure session handling, and credential-stuffing defenses across Shopify, WooCommerce, BigCommerce or custom stacks.
About
A skill for securing ecommerce customer accounts against credential stuffing and account takeover using brute-force protection, MFA, and session controls. A developer uses it when building or auditing a customer account system.
- Per-platform security model split (platform-managed vs configure)
- Layers brute-force protection, MFA, breach-password detection, ATO detection
Account Security by the numbers
- 66 all-time installs (skills.sh)
- Ranked #1,203 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill account-securityAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 66 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Protect customer accounts with brute-force lockouts, MFA/TOTP, secure session handling, and credential-stuffing defenses across Shopify, WooCommerce, BigCommerce or custom stacks.
Files
Account Security
Overview
Customer accounts hold saved payment methods, loyalty points, purchase history, and shipping addresses — making them high-value targets for credential-stuffing attacks and account takeovers. Effective account security layers brute-force protection on the login page, breach-exposed password detection, optional multi-factor authentication (MFA), and anomaly detection for account takeover patterns. The right approach depends heavily on your platform — Shopify manages most security controls at the platform level, while WooCommerce requires additional plugins.
When to Use This Skill
- When building a customer account system from scratch
- When auditing an existing account system for security weaknesses
- When observing credential stuffing attacks (high login failure rates from distributed IPs)
- When adding MFA as an optional or required layer for high-value customers
- When implementing "Sign in with Google/Apple" as a more secure alternative to passwords
Core Instructions
Step 1: Understand your platform's security model
| Platform | What the Platform Handles | What You Need to Configure |
|---|---|---|
| Shopify | SSL/TLS, brute-force protection on login, PCI compliance, server-side security | Customer account settings, whether to require phone verification, Social login apps for Google/Apple sign-in |
| WooCommerce | Basic login form only | Install Wordfence (free) for brute-force protection, limit login attempts, 2FA, and login security monitoring |
| BigCommerce | SSL/TLS, platform-managed account security, basic brute-force protection | Two-factor authentication for admin users; customer-facing 2FA requires an app |
| Custom / Headless | Nothing — you build all security controls | Rate limiting, password hashing, MFA, session management, ATO detection |
Step 2: Platform-specific account security setup
---
Shopify
Shopify handles the majority of customer account security at the platform level — you do not manage SSL, brute-force detection, or password hashing yourself.
Enable new customer accounts (passwordless login): Shopify offers two account experiences: 1. Go to Settings → Customer accounts 2. New customer accounts (recommended): customers log in with a one-time 6-digit code sent to their email or phone; no password to steal or brute-force 3. Classic customer accounts: traditional email/password accounts
Switching to new customer accounts (passwordless) eliminates the most common attack vectors: credential stuffing and brute force.
Two-step verification for admin accounts: 1. Go to Settings → Users and permissions 2. Click on a user → enable Two-step authentication required 3. Shopify supports authenticator apps (TOTP) and SMS for admin 2FA
Social login (Google/Facebook/Apple): Install a social login app from the Shopify App Store:
- Oxi Social Login — supports Google, Facebook, Apple, LinkedIn
- NDNAPPS Social Login — supports Google, Facebook, Apple
These apps add social login buttons to the account creation and login pages.
Detecting suspicious customer activity: Shopify does not expose customer login events for programmatic monitoring. For advanced monitoring, use Shopify's Fraud analysis in orders to detect account takeovers combined with fraudulent purchases.
---
WooCommerce
WooCommerce's built-in login form has no rate limiting, MFA, or advanced security. You must add these via plugins.
Install Wordfence Security (free, recommended): 1. Install and activate Wordfence Security from the WordPress plugin directory 2. Go to Wordfence → Login Security 3. Enable Brute Force Protection:
- Lock out after X failed attempts (default: 20)
- Lock out duration (default: 4 hours)
- Count failures over X minutes (default: 5 minutes)
4. Enable Two-Factor Authentication for admin users (Wordfence supports authenticator app TOTP)
Limit Login Attempts Reloaded (free alternative for just rate limiting): 1. Install Limit Login Attempts Reloaded 2. Configure: lockout after 4 failures, lock for 20 minutes, notify by email after 4 lockouts
Customer-facing 2FA: 1. Install miniOrange 2 Factor Authentication or WP 2FA plugin 2. Configure to allow (or require) 2FA for customers 3. WP 2FA supports TOTP (Google Authenticator, Authy), email codes, and backup codes
Social login for WooCommerce: Install Nextend Social Login (free/premium) — supports Google, Facebook, Apple, and Twitter/X sign-in on WooCommerce login and registration pages.
Compromised password detection: Install WPassword or implement a custom check against the HaveIBeenPwned API during registration and password changes.
---
BigCommerce
Admin two-factor authentication: 1. Go to Settings → Account Settings → Security 2. Enable Two-Step Verification; BigCommerce supports authenticator apps and SMS
Customer account security: BigCommerce does not expose customer-facing 2FA natively. Options:
- Use Stencil (BigCommerce's storefront framework) to add a custom verification step on the customer login page
- Use an identity provider (Auth0, Okta) for customer authentication — this provides full MFA support and credential monitoring
Google/Social sign-in for BigCommerce: Integrate via BigCommerce's customer login API with a social identity provider. Auth0 provides a turnkey solution with a BigCommerce integration.
---
Custom / Headless
For custom storefronts, implement security controls at each layer.
Rate limiting on the login endpoint:
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const redis = Redis.fromEnv();
const ipLimiter = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(20, '15 m'), prefix: 'rl_login_ip' });
const accountLimiter = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(5, '15 m'), prefix: 'rl_login_email' });
export async function checkLoginRateLimit(ip: string, email: string) {
const [ipResult, accountResult] = await Promise.all([
ipLimiter.limit(ip),
accountLimiter.limit(email.toLowerCase()),
]);
if (!ipResult.success) throw new Error('TOO_MANY_REQUESTS_IP');
if (!accountResult.success) throw new Error('ACCOUNT_TEMPORARILY_LOCKED');
}Secure password hashing (Argon2id):
import { hash, verify } from 'argon2';
export const hashPassword = (password: string) =>
hash(password, { type: 2 /* argon2id */, memoryCost: 65536, timeCost: 3, parallelism: 4 });
export const verifyPassword = (storedHash: string, password: string) =>
verify(storedHash, password);TOTP-based MFA:
import { authenticator } from 'otplib';
export function generateMFASecret(): string {
return authenticator.generateSecret(32);
}
export function verifyTOTP(secret: string, token: string): boolean {
authenticator.options = { window: 1 }; // Allow ±30-second drift only
return authenticator.verify({ token, secret });
}Secure session cookies:
// Set session cookies with httpOnly, Secure, SameSite=Strict
response.cookies.set('session_token', token, {
httpOnly: true, // Not accessible to JavaScript — prevents XSS theft
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: 30 * 24 * 60 * 60,
path: '/',
});Send security alert emails for sensitive account changes: Always notify customers via email when:
- Password is changed
- Email address is changed
- A new device or location logs in
- MFA is disabled
Best Practices
- Use passwordless login when possible — Shopify's new customer accounts use one-time codes, eliminating the credential-stuffing attack surface entirely
- Return the same error for "user not found" and "wrong password" — never reveal which was wrong; this prevents user enumeration
- Offer MFA, but only require it for admin and high-value accounts — mandatory MFA for all customers increases abandonment; use it for B2B, admin, and customers with saved payment methods
- Send security alert emails for every sensitive change — password changes, email changes, new device logins, and MFA changes must trigger immediate notification to the customer's confirmed email
- Review and rotate admin credentials quarterly — a compromised admin account can expose all customer data
- Use Social login (Google/Apple) as a more secure alternative — these providers handle password security and may prompt for their own MFA
Common Pitfalls
| Problem | Solution |
|---|---|
| WooCommerce login has no rate limiting | Install Wordfence or Limit Login Attempts Reloaded immediately on any live WooCommerce site |
| Admin account compromised via credential stuffing | Enable 2FA for all admin users; use strong, unique passwords for each admin; never reuse admin credentials across services |
| TOTP codes working indefinitely (custom builds) | Use window: 1 in otplib to accept only ±30-second drift; never accept codes older than 90 seconds |
| Refresh token stored in localStorage | Store refresh tokens in httpOnly cookies only; localStorage is accessible to JavaScript and vulnerable to XSS |
| Rate limit bypass by rotating email variations | Normalize email addresses (lowercase, strip + aliases before the @) before applying per-account rate limiting |
Related Skills
- @secure-checkout
- @fraud-detection
- @gdpr-ecommerce
- @bot-protection
{
"context": "Tests whether the agent implements brute-force login protection using the correct Upstash/Redis packages with the exact rate limit thresholds, progressive delay values, email normalization, and user-enumeration-safe error handling as prescribed by the skill.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Upstash ratelimit package",
"max_score": 8,
"description": "Imports from '@upstash/ratelimit' (not a different rate-limit library)"
},
{
"name": "Upstash Redis package",
"max_score": 6,
"description": "Imports from '@upstash/redis' and uses Redis.fromEnv() to initialize the client"
},
{
"name": "Sliding window algorithm",
"max_score": 6,
"description": "Uses Ratelimit.slidingWindow() (not fixed window or token bucket) for both limiters"
},
{
"name": "Per-IP limit threshold",
"max_score": 8,
"description": "Per-IP limiter is configured with exactly 20 attempts per 15-minute window"
},
{
"name": "Per-account limit threshold",
"max_score": 8,
"description": "Per-account limiter is configured with exactly 5 attempts per 15-minute window"
},
{
"name": "Rate limiter prefixes",
"max_score": 6,
"description": "IP limiter uses prefix 'rl_login_ip' and email limiter uses prefix 'rl_login_email'"
},
{
"name": "Parallel limit checks",
"max_score": 6,
"description": "Both IP and account rate limit checks are executed concurrently (e.g. via Promise.all) rather than sequentially"
},
{
"name": "Progressive delay thresholds",
"max_score": 10,
"description": "Progressive delay returns 0ms for 0 failures, 1000ms for less than 3 failures, 3000ms for less than 5 failures, and 5000ms for 5 or more failures"
},
{
"name": "Failure TTL",
"max_score": 8,
"description": "Login failure counter is stored with a TTL/expiry of 900 seconds (15 minutes)"
},
{
"name": "Email normalization",
"max_score": 8,
"description": "Email address is lowercased before being used as a rate limit key or failure-tracking key"
},
{
"name": "User enumeration prevention",
"max_score": 10,
"description": "The same error message (e.g. 'Invalid credentials') is returned for both unknown-email and wrong-password cases — the response does NOT indicate which was wrong"
},
{
"name": "No localStorage usage",
"max_score": 6,
"description": "The implementation does NOT reference localStorage or sessionStorage for storing any authentication token"
},
{
"name": "Session fixation prevention mentioned",
"max_score": 4,
"description": "README or code comments mention session ID regeneration after successful login to prevent session fixation"
},
{
"name": "Distinct error types",
"max_score": 6,
"description": "Rate limit errors distinguish between IP-blocked and account-locked conditions (different error codes or messages)"
}
]
}
Login Protection Module
Problem/Feature Description
A mid-sized e-commerce platform is observing a surge in failed login attempts across thousands of customer accounts over the past week. The security team suspects a coordinated credential stuffing campaign where attackers are using leaked credential lists from other data breaches. The system currently has no protection beyond a simple password check, and the database is being hammered by login requests.
The engineering team needs a dedicated login protection module for the existing Next.js application. The module should slow down and block malicious actors while keeping the experience smooth for legitimate customers who occasionally mistype their password. The team has already approved use of Upstash (Redis-backed) for rate limiting infrastructure.
Output Specification
Produce the following files:
lib/auth/login-protection.ts— the protection module containing rate limiting and delay logic as TypeScript sourcelib/auth/login-protection.test.ts— unit tests (or integration tests) that verify the behavior of the module, demonstrating the rate limit thresholds and progressive delay values
The TypeScript file should be fully compilable and runnable in a Node.js/Next.js environment. Add a short README.md explaining the module's design decisions and the thresholds chosen.
{
"context": "Tests whether the agent uses the correct Argon2 variant and parameters for password hashing, implements the HIBP breach check with k-anonymity correctly, uses otplib with the right secret length and TOTP window setting, generates the correct number of backup codes with hashed storage, and encrypts the TOTP secret at rest.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Argon2 package used",
"max_score": 8,
"description": "Imports from the 'argon2' package (not bcrypt, scrypt, or other password hashing library)"
},
{
"name": "Argon2id variant",
"max_score": 8,
"description": "Uses 'argon2id' as the hashing type/variant (not argon2i or argon2d)"
},
{
"name": "Argon2 memoryCost",
"max_score": 8,
"description": "memoryCost is set to exactly 65536 (64MB)"
},
{
"name": "Argon2 timeCost and parallelism",
"max_score": 6,
"description": "timeCost is set to 3 AND parallelism is set to 4"
},
{
"name": "HIBP k-anonymity",
"max_score": 8,
"description": "HIBP breach check sends only the first 5 characters of the SHA1 hash to the API (not the full hash or full password)"
},
{
"name": "HIBP Add-Padding header",
"max_score": 6,
"description": "HIBP API request includes the header 'Add-Padding: true'"
},
{
"name": "otplib used",
"max_score": 6,
"description": "Imports authenticator from 'otplib' for TOTP generation and verification"
},
{
"name": "32-byte TOTP secret",
"max_score": 8,
"description": "TOTP secret is generated with 32 bytes (e.g., authenticator.generateSecret(32))"
},
{
"name": "TOTP window setting",
"max_score": 6,
"description": "TOTP verification uses window: 1 to allow ±30-second drift"
},
{
"name": "8 backup codes generated",
"max_score": 6,
"description": "Exactly 8 backup codes are generated during MFA setup (not more, not fewer)"
},
{
"name": "Backup codes hashed",
"max_score": 8,
"description": "Backup codes are stored as hashed values (using Argon2 or equivalent), NOT as plain text strings"
},
{
"name": "TOTP secret encrypted",
"max_score": 8,
"description": "The TOTP secret is stored encrypted (e.g., AES-256-GCM or similar), NOT as plain text"
},
{
"name": "MFA not enabled until verified",
"max_score": 6,
"description": "MFA enabled flag is set to false at setup and only activated after a successful first verification"
},
{
"name": "Backup code single-use",
"max_score": 8,
"description": "A backup code is deleted or invalidated after it is successfully used (not reusable)"
}
]
}
Customer Account Security: Password and MFA Module
Problem/Feature Description
A growing online retailer wants to strengthen how it stores customer passwords and provide optional two-factor authentication. The security team has just completed an audit and found two gaps: passwords are currently stored with bcrypt at a work factor that may be insufficient for modern hardware, and there is no check to warn customers when they choose passwords that have appeared in public data breaches. Additionally, customers have been requesting an authenticator app option ever since a competitor suffered an account takeover incident that made the news.
The team wants a self-contained TypeScript module that handles both concerns: secure password storage with breach detection, and a complete TOTP-based MFA enrollment and verification flow (including backup codes for account recovery). The module will be imported into the Next.js API routes for registration, login, and account settings pages.
Output Specification
Produce the following files:
lib/auth/password.ts— password hashing, verification, and breach-checking functionslib/auth/mfa.ts— MFA setup, TOTP verification, and backup code management functionslib/auth/auth.test.ts— tests that demonstrate the behavior of both modules (e.g., hashing round-trip, breach check request structure, TOTP verification, backup code exhaustion)README.md— brief explanation of library choices and configuration parameters selected
All TypeScript code should be complete and importable. Mock or stub any database calls (e.g., use a simple in-memory object) so the tests can run without a real DB.
{
"context": "Tests whether the agent uses jose for JWT creation with HS256 and a 15-minute access token lifetime, issues a 30-day refresh token as a UUID, stores only the hashed refresh token in the database, implements rotation with replay detection that revokes the entire session family, and sets cookies with the correct security attributes.",
"type": "weighted_checklist",
"checklist": [
{
"name": "jose package",
"max_score": 8,
"description": "Imports SignJWT (and/or jwtVerify) from the 'jose' package (not jsonwebtoken or other JWT library)"
},
{
"name": "HS256 algorithm",
"max_score": 6,
"description": "Access tokens are signed using the HS256 algorithm (setProtectedHeader({alg: 'HS256'}))"
},
{
"name": "15-minute access token",
"max_score": 8,
"description": "Access token expiry is set to exactly 15 minutes (e.g., setExpirationTime('15m'))"
},
{
"name": "30-day refresh token",
"max_score": 6,
"description": "Refresh token session lifetime is set to 30 days (expiresAt = now + 30 * 24 * 60 * 60 * 1000 ms or equivalent)"
},
{
"name": "Refresh token as UUID",
"max_score": 6,
"description": "Refresh token value is generated using crypto.randomUUID() (not a JWT or a random hex string)"
},
{
"name": "Hashed refresh token storage",
"max_score": 10,
"description": "Only the hashed version of the refresh token is stored in the database (refreshTokenHash); the raw token is never persisted"
},
{
"name": "Session metadata stored",
"max_score": 6,
"description": "Session record includes ip, userAgent, expiresAt, and lastUsedAt fields"
},
{
"name": "Refresh token rotation",
"max_score": 10,
"description": "Each successful refresh issues a new access token AND a new refresh token (old token is marked used/invalidated)"
},
{
"name": "Replay attack detection",
"max_score": 10,
"description": "If a refresh token that is already marked as used is presented again, all sessions for that customer are revoked"
},
{
"name": "Security team alert on replay",
"max_score": 6,
"description": "Replay detection triggers an alert/notification to the security team (not just silent revocation)"
},
{
"name": "httpOnly and secure cookie flags",
"max_score": 8,
"description": "Refresh token cookie has both httpOnly: true and secure: true attributes"
},
{
"name": "SameSite=Strict cookie",
"max_score": 8,
"description": "Refresh token cookie has sameSite: 'strict' attribute"
},
{
"name": "Cookie path restriction",
"max_score": 6,
"description": "Refresh token cookie has path set to '/api/auth' (not '/' or omitted)"
},
{
"name": "No localStorage usage",
"max_score": 2,
"description": "Code does NOT store any token in localStorage or sessionStorage"
}
]
}
Secure Session Management Module
Problem/Feature Description
An e-commerce startup is redesigning its authentication layer after a security review flagged that all customer tokens are stored as long-lived JWTs with no mechanism to revoke them. Once a token is issued, it remains valid indefinitely unless the secret key changes — which invalidates all sessions globally. The team also discovered that a support ticket from last month was actually a stolen session: a customer's refresh token had been lifted from their browser's localStorage, giving the attacker permanent access.
The engineering team wants a new session management module that balances security with usability: short-lived access credentials that limit the blast radius of a token leak, paired with a long-lived rotation mechanism that detects and responds to replay attacks. The solution should integrate naturally with a Next.js API routes architecture and set cookies in a way that blocks common web attack vectors.
Output Specification
Produce the following files:
lib/auth/session.ts— TypeScript module implementing session creation, access token generation, and refresh token rotation logiclib/auth/session.test.ts— tests that demonstrate session creation behavior, rotation, replay detection (what happens when a used token is presented again), and cookie configurationREADME.md— explains the token lifetimes, rotation strategy, replay attack detection behavior, and cookie security settings
All database interactions may be stubbed with an in-memory store so tests run without a real database. Show the cookie-setting logic clearly in the code or tests.
{
"name": "finsi/account-security",
"version": "0.1.0",
"summary": "Brute-force protection, MFA, session management for customer accounts",
"skills": {
"account-security": {
"path": "SKILL.md"
}
}
}