
Description
Use when auditing or hardening a Meteor 3 application. Triggers on missing check() on method arguments, missing this.userId guards on publications, browser-policy CSP, DDPRateLimiter rules, oauth-encryption via Accounts.config oauthSecretKey, audit-argument-checks, allow/deny legacy patterns, BrowserPolicy.content.disallowInlineScripts, BrowserPolicy.framing.disallow. Use this skill when the user asks about hardening, asks about a security review, or asks about CSP for a third-party script (Stripe, Google Maps, fonts).
SKILL.md
Meteor security
Meteor's security model is opinionated: the server holds authority, the client cannot be trusted, and the only places that filter data before it reaches users are methods (write paths) and publications (read paths).
Decision flow
- Audit every method: does it
check()every argument and guard onthis.userIdwhen authentication matters? - Audit every publication: does it filter by
this.userId(when user-specific) and project columns withfields? - Add
audit-argument-checksin dev to catch missingcheck(). - Add
browser-policyand configure CSP. - Add
DDPRateLimiterrules for sensitive methods (login, password reset, resource creation). - If the app uses OAuth, set
oauthSecretKeyto encrypt provider secrets at rest. - Remove
allow/denyrules. They are legacy and easy to misuse; use methods instead.
Method guard checklist
import { Meteor } from "meteor/meteor";
import { check, Match } from "meteor/check";
Meteor.methods({
async updateProfile(payload) {
check(payload, { displayName: String, bio: Match.Optional(String) });
if (!this.userId) {
throw new Meteor.Error("not-authorized");
}
await Meteor.users.updateAsync(this.userId, { $set: { profile: payload } });
},
});
Reject any method that does not match: check on every argument, userId
gate when needed, Meteor.Error(code, reason) for failures, *Async
Mongo on the server.
Publication guard checklist
Meteor.publish("items.mine", function () {
if (!this.userId) return this.ready();
return Items.find(
{ ownerId: this.userId },
{ fields: { title: 1, qty: 1 }, limit: 200 },
);
});
Reject any publication that returns an unbounded cursor, omits the field projection, or skips a userId filter on user-specific data.
CSP via browser-policy
meteor add browser-policy
// server top-level or inside Meteor.startup
import { BrowserPolicy } from "meteor/browser-policy-common";
import { Meteor } from "meteor/meteor";
Meteor.startup(async () => {
await BrowserPolicy.content.disallowInlineScripts();
BrowserPolicy.content.disallowEval();
BrowserPolicy.framing.disallow();
});
BrowserPolicy is server-only. Configure it during module initialization or
startup so every request receives one deterministic process-wide policy. The
current implementation invalidates its cached CSP after a mutation, but do not
mutate this global policy per request or per user. See
references/browser-policy-csp.md for recipes (Stripe, Google Maps,
fonts, inline-style allowance).
DDPRateLimiter for sensitive methods
import { DDPRateLimiter } from "meteor/ddp-rate-limiter";
DDPRateLimiter.addRule(
{
type: "method",
name: "login",
clientAddress: () => true,
},
5,
60000, // 5 attempts per 60s, per IP
);
Only matcher fields contribute to the rate-limit bucket key. Without
clientAddress, connectionId, or userId, every matching caller shares one
global bucket. Meteor 3.5+ permits async matcher functions for database-backed
decisions; keep their queries fast because the connection waits for them. On
Meteor 3.0 through 3.4, matchers must stay synchronous. Use a fixed rule,
precomputed synchronous state, or upgrade rather than awaiting Mongo there.
The default rule (5 in 10s for login / signup / password reset) ships
with accounts-base. Remove with Accounts.removeDefaultRateLimit()
only if you replace it.
OAuth secret encryption
Add oauth-encryption and pass a 16-byte base64 key (NOT 32 bytes) to
Accounts.config at module top level (not inside Meteor.startup):
meteor node -e "console.log(require('crypto').randomBytes(16).toString('base64'))"
import { Accounts } from "meteor/accounts-base";
Accounts.config({
oauthSecretKey: Meteor.settings.oauthSecretKey,
});
At startup, accounts-oauth seals an unsealed provider application secret at
ServiceConfiguration.configurations.secret. Provider packages also seal
supported per-user token fields, such as services.github.accessToken or
Twitter's accessTokenSecret. There is no generic
Meteor.users.services.<provider>.secret field. Inspect the provider schema
before asserting which user credential is encrypted.
audit-argument-checks
meteor add audit-argument-checks
Throws if any method or publication runs without check() covering
every argument. Methods that legitimately accept arbitrary input declare
this explicitly:
Meteor.methods({
rawLog(...args) {
check(args, [Match.Any]);
// ...
},
});
Anti-patterns
Collection.allow/Collection.denyrules. Legacy; easy to combine into a soft-fail. Replace with methods.Meteor.settings.public.<secret>. The client seespublic. Move secrets to the top level ofsettings.json.- Publish the entire
Meteor.userscollection. Always project (e.g.fields: { username: 1, profile: 1 }) and filter. Publish email only to the owning user or another explicitly authorized audience. - Use
BrowserPolicy.content.allowOriginForAllfor a third-party script. It grants the origin to every current content directive. Allow only the script, frame, connect, image, style, or font directives the integration needs. - Methods that accept callback-shaped arguments. Functions cannot travel over DDP.
- Call
Accounts.config({ oauthSecretKey })insideMeteor.startup. Must be at module top level so it loads before the OAuth packages read it.
See also
references/method-and-publish-guards.mdreferences/browser-policy-csp.mdreferences/eval-cases.md- Related skills:
meteor-methods,meteor-pubsub,meteor-accounts.
More skills from the agent-skills repository
View all 14 skillsmeteor-accounts
implement authentication in Meteor apps
Aug 28AuthAuthenticationMeteorOAuthmeteor-blaze
build and debug Meteor Blaze interfaces
Aug 28FrontendMeteorWeb Developmentmeteor-community-packages
manage Meteor community packages
Aug 28EngineeringMeteormeteor-debugging
diagnose failures in Meteor 3 applications
Aug 28DebuggingMeteorWebSocketsmeteor-deployment
deploy Meteor 3 applications
Aug 28DeploymentDockerKubernetesMeteormeteor-methods
author and debug Meteor methods
Aug 28API DevelopmentBackendMeteor
More from Meteor
View publishermeteor-modern-build-stack
configure Meteor 3 modern build stacks
agent-skills
Aug 28BuildMeteorPerformancemeteor-mongo-minimongo
author and debug Meteor MongoDB queries
agent-skills
Aug 28DatabaseDebuggingMeteorMongoDBmeteor-pubsub
author and debug Meteor publications
agent-skills
Aug 28BackendMeteorReal-timemeteor-react
build and debug Meteor React interfaces
agent-skills
Aug 28FrontendMeteorReactWeb Developmentmeteor-testing
write and repair Meteor test harnesses
agent-skills
Aug 28MeteorQATesting