MkExt
Build a browser extension with MkExt and connect it to TanStarter authentication
MkExt is a browser extension template built with WXT, React, and Base UI. It includes popup, options, side panel, new-tab, DevTools, content scripts, and a background worker, so you can build a product extension from one TypeScript codebase.
MkExt is a client template: it does not include a database, email service, or an OAuth server. Pair it with TanStarter when your extension needs accounts, sessions, and protected product APIs. The TanStarter main branch includes the extension-authentication support described here.
Website: mkext.dev
Source: MkThingsHQ/mkext
MkExt source code is separate from the TanStarter template benefits. Start with the MkExt repository for extension code, then use this guide to connect it to your TanStarter application.
What the integration provides
The two projects share one TanStarter user account and session model. MkExt stores the product bearer token in extension storage; it does not use the website's cross-site cookies.
For Google sign-in, Chrome's Identity API obtains a short-lived Google access token. TanStarter validates that its audience is your Chrome Extension OAuth client, retrieves the Google profile, and creates or links the TanStarter user before returning a TanStarter bearer token. Do not use the Google access token as your product session or persist it as one.
Prerequisites
- A TanStarter application based on the current
mainbranch, with its database and basic authentication configured. See Authentication and Environment Configuration. - An MkExt checkout with Bun installed.
- A stable Chrome extension ID.
- A Google Cloud project and OAuth consent screen if you want Google sign-in. Email/password authentication does not need Google configuration.
1. Set up MkExt
Copy MkExt's .env.example to .env and point it at your TanStarter application's public origin:
# Build-time public value. It is included in the extension bundle.
VITE_AUTH_URL="https://app.example.com"
# Needed only for Chrome Google sign-in. This is public, not a client secret.
VITE_GOOGLE_EXTENSION_CLIENT_ID="your-extension-client-id.apps.googleusercontent.com"VITE_AUTH_URL must be the origin only—do not append /api/auth. MkExt derives the Better Auth and Google-token exchange endpoints from it. Because every VITE_* value is bundled into the extension, never put GOOGLE_CLIENT_SECRET, BETTER_AUTH_SECRET, a database key, or another server secret in this file.
The MkExt manifest adds the configured auth origin as a host permission. When VITE_GOOGLE_EXTENSION_CLIENT_ID is set, its Chrome manifest also declares the identity permission, the OAuth client ID, and the openid, email, and profile scopes.
2. Get the stable Chrome extension ID
Google's Chrome Extension OAuth client is tied to an extension ID. MkExt's WXT configuration includes a manifest public key so its regular Chrome build has a deterministic ID. Build and load that exact package before creating the Google client:
cd mkext
bun install
bun run buildOpen chrome://extensions, enable Developer mode, select Load unpacked, and choose build/chrome-mv3. Copy the extension ID displayed by Chrome.
Do not test Google sign-in only in a temporary development profile. If you change the manifest key or create a different extension package, Chrome gives it a different ID. Update the Google client and TanStarter's trusted-origin setting together, then reload the extension.
3. Configure Google OAuth (optional)
In the Google Cloud project that owns your product OAuth settings:
- Complete the OAuth consent-screen configuration for your application.
- Create a Chrome Extension OAuth 2.0 client and enter the ID copied from
chrome://extensions. - Copy that client's ID into both
VITE_GOOGLE_EXTENSION_CLIENT_IDin MkExt andGOOGLE_EXTENSION_CLIENT_IDin TanStarter. - Keep TanStarter's regular Google OAuth client ID and client secret configured as well. The current TanStarter Google provider is enabled by
websiteConfig.auth.enableGoogleLogintogether withGOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRET; the extension client is an additional accepted token audience, not a replacement for the website client.
Use the Chrome Extension client with Chrome only. MkExt omits its Chrome-only oauth2 manifest configuration for Firefox because Firefox does not provide the same identity.getAuthToken() flow.
4. Configure TanStarter
Add the extension origin and OAuth values to TanStarter's server runtime environment. For local development, use .env.local; for Cloudflare Workers, set secrets with wrangler secret put (or your deployment secret manager).
# Required for Better Auth in production.
BETTER_AUTH_SECRET="generate-a-strong-secret"
# Exact comma-separated origins allowed to authenticate cross-origin.
BETTER_AUTH_TRUSTED_ORIGINS="chrome-extension://<your-extension-id>"
# TanStarter's regular Google web OAuth client. Keep these server-only.
GOOGLE_CLIENT_ID="your-web-client-id.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="your-web-client-secret"
# Must exactly equal MkExt's VITE_GOOGLE_EXTENSION_CLIENT_ID.
GOOGLE_EXTENSION_CLIENT_ID="your-extension-client-id.apps.googleusercontent.com"Also ensure Google login is enabled in src/config/website.ts:
auth: {
enable: true,
enableGoogleLogin: true,
// Enable this as well when offering email/password sign-in in the extension.
enableCredentialLogin: true,
}TanStarter uses this configuration to:
- enable Better Auth's bearer-token plugin;
- allow only the configured extension origin in Better Auth and CORS;
- accept
Authorizationand expose theset-auth-tokenresponse header; - verify that a Google extension token belongs to
GOOGLE_EXTENSION_CLIENT_ID; and - create a normal TanStarter session for the returned product bearer token.
For a Firefox package, a moz-extension://<uuid> origin can be added when needed. Firefox assigns it per profile, so TanStarter supports the explicit moz-extension://* opt-in. This only solves the origin boundary; it does not make Chrome's Google Identity API available in Firefox.
5. Verify the complete flow
Build or reload the Chrome extension after changing its .env, then test against the actual TanStarter deployment:
- Open the extension login page and register or sign in with email/password. Confirm that a new or existing TanStarter account is used.
- With Google configured, select Google sign-in and complete Chrome's account prompt.
- Confirm the extension shows the TanStarter user after it synchronizes its session.
- Call one protected TanStarter API from the extension and confirm it accepts the bearer session.
- Log out, reopen the extension, and confirm that the local bearer session is cleared.
When diagnosing failures, use the response deliberately: 403 usually means the extension ID is missing or incorrect in BETTER_AUTH_TRUSTED_ORIGINS; 503 means GOOGLE_EXTENSION_CLIENT_ID is absent on TanStarter; a Google audience error means the two extension client-ID values or the Chrome extension ID do not match.
A successful build proves only that the extension package was generated. Google account selection, OAuth consent, the extension ID, CORS, and the deployed TanStarter secrets must be checked in a normal Chrome profile before treating Google sign-in as ready for release.
Next Steps
Last updated on
