Skip to content
Back to skills

Chrome Extension Auth

ASecurity

Add user accounts to a Chrome MV3 extension with a backend. Covers Google SSO via chrome.identity.launchWebAuthFlow (OIDC id_token + server nonce, JWKS verification with jose) and manual email/password sign-in (argon2id hashing, email verification, password reset, account linking). Use for "login in chrome extension", "Google sign in extension", "launchWebAuthFlow", "chrome.identity", "OAuth chrome extension", "email password auth extension", "SSO", "session JWT extension", "sign in with Goog...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 24, 2026
ai-agentsrustgosqlnodedebugginggitapibackendsecurity

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 6 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add thomaslimited/chrome-extension-skills --skill chrome-extension-auth --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Chrome Extension Auth?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Chrome Extension Auth
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/thomaslimited-chrome-extension-auth/badge)](https://www.skillsdirectory.com/skills/thomaslimited-chrome-extension-auth)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: chrome-extension-auth
description: Add user accounts to a Chrome MV3 extension with a backend. Covers Google SSO via chrome.identity.launchWebAuthFlow (OIDC id_token + server nonce, JWKS verification with jose) and manual email/password sign-in (argon2id hashing, email verification, password reset, account linking). Use for "login in chrome extension", "Google sign in extension", "launchWebAuthFlow", "chrome.identity", "OAuth chrome extension", "email password auth extension", "SSO", "session JWT extension", "sign in with Google MV3", "auth chrome-extension:// CORS", "extension ID redirect URI mismatch". Includes session storage (storage.local vs storage.session), 401/token-refresh handling, JWT secret rotation, and server-side revocation. Does NOT cover payments/entitlements, Chrome Web Store listing, or build/publish pipelines — see sibling skills.
license: MIT
---

# Chrome Extension Auth

Add sign-in and sessions to a Manifest V3 extension backed by a server. Two proven paths, one
shared session contract.

## Scope

This skill covers: Google SSO (OAuth "Web application" client + `launchWebAuthFlow`), email/password
sign-in, session issuance and verification, extension-side storage and API calls, CORS, rate
limiting, and revocation.

This skill does **not** cover:
- Payments, subscriptions, entitlements → `chrome-extension-payments`
- Chrome Web Store listing content, screenshots, review policy → `chrome-web-store-listing`
- Extension scaffolding, build tooling, content scripts → `chrome-extension-dev`
- Packaging and publishing → `chrome-extension-deploy`

## Decision table

| Situation | Choose | Why |
|---|---|---|
| Want the fastest signup, no password support burden | Google SSO only | No password storage/reset flow to build or secure |
| Users without Google accounts, enterprise, or privacy-sensitive audience | Email/password (or passwordless OTP) | Not everyone has/wants a Google account |
| Want maximum reach, willing to build both | Both, with account linking | Same email can arrive via either path — link, don't duplicate |
| Want passwordless but no OAuth complexity | Email OTP (magic code) | Simpler than full password flow; still needs an email provider |

Both paths **must** converge on the same session contract: a server-issued session JWT, verified
the same way, stored the same way in the extension. Do not build two different session formats.

## Workflow A — Google SSO

1. Register a Google OAuth **Web application** client (not "Chrome App" — see
   `references/google-sso-extension.md#oauth-client-setup`). Add the redirect URI
   `https://<extension-id>.chromiumapp.org/` for **both** the dev extension ID (pinned via a
   manifest `key`) and the Chrome Web Store ID (assigned after first publish, unknown until then).
2. In the extension manifest, add the `identity` permission and pin a dev key so the extension ID
   is stable across rebuilds — see `references/google-sso-extension.md#manifest-and-dev-key`.
3. Implement `beginGoogleSignIn`: fetch a server-issued nonce, build the Google authorization URL
   with `response_type=id_token`, launch `chrome.identity.launchWebAuthFlow`, parse the ID token
   out of the redirect fragment. Full code in `references/google-sso-extension.md#client-flow`.
4. On the server, verify the ID token against Google's JWKS with `jose` (`aud`, `iss`, `exp`,
   `nonce`, `email_verified`), burn the nonce, upsert the user, issue a session JWT. Full code in
   `references/google-sso-backend.md`.
5. Store the session in `chrome.storage.local` from the background script only; attach it as a
   `Bearer` header on every API call made from the background script. See
   `references/google-sso-extension.md#session-and-api-calls`.
6. Handle sign-out and 401s (session invalid/expired → clear and prompt re-login). See
   `references/sessions-cors-security.md#401-and-refresh`.

Read `references/troubleshooting.md` before debugging a "sign-in opens and closes immediately" or
"nonce mismatch" report — both have a specific, known root cause.

## Workflow B — Email/password

1. Add schema: `password_hash`, `password_algo` on `users`; `email_verifications` and
   `password_reset_tokens` tables. SQL in `references/email-password.md#schema`.
2. Signup: create an unverified user, send a 6-digit code (hashed, expiring, attempt-limited) via
   an email provider behind an interface (Resend/SES/SMTP). Code in
   `references/email-password.md#signup-and-verification`.
3. Password hashing: argon2id by default; `node:crypto` `scrypt` fallback when a native module is
   not an option. Never roll your own KDF loop count. Code in
   `references/email-password.md#password-hashing`.
4. Login: constant-time password compare, generic error message ("invalid email or password") for
   both "no such user" and "wrong password", per-IP and per-account throttling. Code in
   `references/email-password.md#login`.
5. Password reset: request → emailed single-use token (hashed, expiring) → reset endpoint that
   invalidates the token and bumps `token_version` (see revocation below). Code in
   `references/email-password.md#password-reset`.
6. Account linking: if a Google sign-in's verified email matches an existing password account,
   link `google_sub` onto that row instead of creating a duplicate — only when
   `email_verified` is true. Never auto-link an unverified email. Code in
   `references/email-password.md#account-linking`.
7. Issue the **same** session JWT shape as Workflow A step 4 so extension-side code (storage, API
   client, `use-session` hook) doesn't fork per auth method.
8. Build popup/options forms (signup, login, verify, forgot/reset password) that submit through
   the background script, same as billing messages — never let a content script or a form
   directly hold credentials outside the extension's own pages. UI notes in
   `references/email-password.md#ui-forms`.

Alternative: skip passwords entirely with email OTP (passwordless) — same schema minus
`password_hash`, same session issuance. See `references/email-password.md#passwordless-otp-simpler-alternative`.

## Security checklist

- [ ] `identity` permission present; `optional_permissions` unaffected by auth
- [ ] OAuth client is "Web application" type, both dev and store extension IDs registered as
      redirect URIs
- [ ] Nonce is **server-issued**, single-use, and expires (10 min is reasonable) — never trust a
      client-generated nonce for replay protection
- [ ] ID token verified against live JWKS (`aud`, `iss`, `exp`, `nonce`, `email_verified`) — never
      decode without verifying
- [ ] Session JWT is HS256 (or better) with a real expiry, signed with a secret ≥32 random bytes,
      never the placeholder default
- [ ] Session stored in `chrome.storage.local`, read/written only by the background script, never
      exposed to content scripts or web pages
- [ ] Every extension→server call carries `Authorization: Bearer <token>` and runs from the
      background script only
- [ ] `runtime.onMessage` handler validates the sender is the extension's own page before acting on
      any auth/account message (see `references/sessions-cors-security.md#sender-validation`)
- [ ] Server CORS allowlist matches exact `chrome-extension://<id>` origins — no wildcard, no
      prefix match
- [ ] Rate limiting on nonce issuance, token exchange, login, signup, and password reset request
- [ ] Passwords hashed with argon2id or scrypt with real cost parameters — never plaintext, never
      unsalted, never fast hashes (MD5/SHA-1/SHA-256 alone)
- [ ] Password/email-verification/reset codes are hashed at rest, expiring, and attempt-limited
- [ ] Generic error messages on login (do not reveal whether the email exists)
- [ ] Account linking only on a **verified** email match
- [ ] A `token_version` (or equivalent) column exists so sessions can be revoked server-side
      without waiting for natural expiry
- [ ] Client secret (if any OAuth flow needs one) never ships in the extension bundle — only the
      public client ID does

## Reference files

- `references/google-sso-extension.md` — manifest, dev key, `launchWebAuthFlow` client flow,
  session storage, background-only API calls, sender validation
- `references/google-sso-backend.md` — Hono routes, `jose` JWKS verification, nonce issuance,
  session JWT issuance/verification, user upsert, CORS, rate limiting
- `references/email-password.md` — full email/password design: schema, signup/verification,
  hashing, login, reset, account linking, UI forms, passwordless OTP alternative
- `references/sessions-cors-security.md` — storage.local vs storage.session, secret rotation,
  revocation, 401/refresh handling, sender validation detail
- `references/troubleshooting.md` — known failure modes and their fixes, including the
  extension-ID redirect-URI mismatch bug

Files in this skill

  • SKILL.md8.7 KB
  • references/email-password.md14.1 KB
  • references/google-sso-backend.md10.2 KB
  • references/google-sso-extension.md10.2 KB
  • references/sessions-cors-security.md6.9 KB
  • references/troubleshooting.md5.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.