agentleFS
meteormeteor/agent-skills/skills/meteor-accounts/SKILL.md

meteor-accounts

Skill10 starsChanged 3 months agoMITView on GitHub

Use when wiring up authentication in a Meteor 3 app. Triggers on accounts-password, accounts-base, OAuth (Google, Facebook, GitHub, Apple, Twitter, Meetup, Weibo), accounts-2fa, accounts-passwordless, ServiceConfiguration.configurations.upsertAsync, Accounts.createUserAsync, Accounts.setPasswordAsync, Accounts.forgotPassword, Accounts.resetPassword, Accounts.verifyEmail, useHttpOnlyCookies, clientStorage, Meteor.loginWithPasswordAnd2faCode, email verification. Use this skill when the user asks about signups, signins, or asks about token storage vs HttpOnly cookies.

  • Reads credentials

1 other project uses a version of this, 1 of them edited.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Do not set forbidClientAccountCreation: true when the client signup form calls Accounts.createUser or Accounts.createUserAsync.

    line 65
  2. Distinctive:

    Default token storage is Web Storage.

    line 95
  3. Very specific:

    After restart and login, Meteor.loginToken* no longer appears in localStorage.

    line 122
  4. Very specific:

    Add accounts-passwordless.

    line 212
  5. Very specific:

    Run Accounts.config({ ... }) inside a deferred path.

    line 262

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Meteor accounts

accounts-base plus a flavor package (accounts-password, accounts-google, etc.). Meteor stores users in Meteor.users and ships the client a resume token mapped to that document.

Decision flow

  1. Username + password? Add accounts-password.
  2. Social login? Add accounts-base plus the provider package (e.g. accounts-google) and configure the service.
  3. Magic-link? Add accounts-passwordless.
  4. 2FA? Layer accounts-2fa on top of accounts-password.
  5. Token storage on the client? Default is Web Storage. Meteor 3.3+ supports an HttpOnly cookie flow; see the section below.

For a custom users collection, configure both runtimes before app reads or validators. The 3.6 RC fixes collection setup and publication lookup; review collection identity and client-write policy before changing profile rules.

Username + password

// server/accounts.js
import { Accounts } from "meteor/accounts-base";
import { Meteor } from "meteor/meteor";

Meteor.startup(() => {
  Accounts.config({
    sendVerificationEmail: true,
  });

  Accounts.emailTemplates.siteName = "My App";
  Accounts.emailTemplates.from = "no-reply@example.com";
});

The from address is required; Meteor 3.5+ logs a server warning if you omit it.

Do not set forbidClientAccountCreation: true when the client signup form calls Accounts.createUser or Accounts.createUserAsync. The server rejects that request with 403 Signups forbidden. For invite-only or administrator provisioning, set the option on both client and server, remove the public signup UI, and call Accounts.createUserAsync only from trusted server code after its own authorization check.

// client/signin.js (Meteor 3.5+)
import { Meteor } from "meteor/meteor";
import { Accounts } from "meteor/accounts-base";

async function signUp({ email, password }) {
  await Accounts.createUserAsync({ email, password });
}

async function signIn({ email, password }) {
  await Meteor.loginWithPasswordAsync(email, password);
}

Client Accounts.createUser(options, callback) also remains supported. Meteor.loginWithPasswordAsync was added in Meteor 3.5; on older 3.x use the callback form of Meteor.loginWithPassword or wrap it in a Promise.

On the server use await Accounts.createUserAsync(options) and await Accounts.setPasswordAsync(userId, newPassword).

HttpOnly cookies (Meteor 3.3+)

Default token storage is Web Storage. To move the resume token into an HttpOnly cookie and keep the client tab in-memory only:

// server
Meteor.startup(() => {
  Accounts.config({
    clientStorage: "none",
    useHttpOnlyCookies: true,
  });
});
// settings.json (public; surfaces the same flags to the client)
{
  "public": {
    "packages": {
      "accounts": {
        "clientStorage": "none",
        "useHttpOnlyCookies": true
      }
    }
  }
}

After restart and login, Meteor.loginToken* no longer appears in localStorage; the browser receives an HttpOnly meteor_login_token cookie. Each tab keeps its own in-memory credentials. DDP authentication still reads the resume token into memory; this does not protect it from a malicious script already executing in the application origin.

With accounts-base@3.3.1 (Meteor 3.5.2), the server handles cookie endpoints only when it is opted in too. A client-only setting can therefore produce an HTML response or 404 from later handlers. The /set body limit is 4096 bytes; larger requests return HTTP 413. See cookie endpoint troubleshooting for the routes, version boundary, and diagnostic checks. Meteor 3.6-beta.1's accounts-base@3.4.0-beta360.1 also requires trusted origins and valid tokens, uses SameSite=Strict, and adds a separate per-address endpoint rate limit. Review custom callers and cross-site entry flows before upgrading. The RC uses accounts-base@3.4.0-rc360.0; distinguish invalid sessions from transient or rate-limit responses before retrying or discarding credentials.

OAuth (Google example)

// server/oauth.js
import { ServiceConfiguration } from "meteor/service-configuration";

await ServiceConfiguration.configurations.upsertAsync(
  { service: "google" },
  {
    $set: {
      clientId: process.env.GOOGLE_CLIENT_ID,
      secret: process.env.GOOGLE_CLIENT_SECRET,
      loginStyle: "popup",
    },
  },
);
// client
Meteor.loginWithGoogle(
  { requestPermissions: ["email", "profile"] },
  (err) => { if (err) console.error(err); },
);

loginStyle: "redirect" for mobile or environments without window.close / window.opener. Each provider ships its own accounts-<service> package: accounts-google, accounts-github, accounts-facebook, accounts-twitter, accounts-apple, accounts-meetup, accounts-weibo.

To encrypt OAuth secrets at rest, add oauth-encryption and pass oauthSecretKey to Accounts.config. This seals the provider application secret in ServiceConfiguration.configurations.secret and the supported provider-specific user token fields. It does not create a generic Meteor.users.services.<provider>.secret field. See meteor-security.

Email verification and reset

// client
Accounts.forgotPassword({ email }, (err) => { ... });

// reset page
Accounts.resetPassword(token, newPassword, (err) => { ... });

// verify email
Accounts.verifyEmail(token, (err) => { ... });

Customize the URL pattern that lands in the email:

Accounts.urls.resetPassword = (token) =>
  Meteor.absoluteUrl(`reset/${token}`);
Accounts.urls.verifyEmail = (token) =>
  Meteor.absoluteUrl(`verify/${token}`);

Both setters accept async functions too (Meteor 3.x).

Customize the message:

Accounts.emailTemplates.resetPassword.subject = () => "Reset your password";
Accounts.emailTemplates.resetPassword.text = (user, url) =>
  `Reset link: ${url}`;

Passwordless

Add accounts-passwordless. Request a one-time code or link, then submit the code with the same selector:

Accounts.requestLoginTokenForUser(
  {
    selector: { email },
    userData: { email },
    options: { userCreationDisabled: false },
  },
  (error) => { if (error) console.error(error); },
);

Meteor.passwordlessLoginWithToken(
  { email },
  token,
  (error) => { if (error) console.error(error); },
);

Set userCreationDisabled: true for sign-in-only flows. Configure tokenSequenceLength, loginTokenExpirationHours, and the Accounts.emailTemplates.sendLoginToken template on the server. Treat the token-request method as an abuse-sensitive endpoint and rate-limit repeated requests. With Meteor 3.6-beta.1's accounts-passwordless@3.1.2-beta360.1 and accounts-base@3.4.0-beta360.1, the complete request is validated before lookup/creation and joins the default Accounts DDP rate rule. See passwordless request boundaries for custom callers and earlier packages. The RC pairs accounts-passwordless@3.1.2-rc360.0 with accounts-base@3.4.0-rc360.0; retain the default limiter and handle callback errors without an immediate retry loop.

2FA

accounts-2fa adds TOTP. The login flow:

  1. Client calls Meteor.loginWithPasswordAsync(user, password) on Meteor 3.5+, or the callback form of Meteor.loginWithPassword on older 3.x.
  2. Server rejects with error.error === 'no-2fa-code'.
  3. Client prompts for a code, retries with Meteor.loginWithPasswordAnd2faCode(user, password, code, cb).

Anti-patterns

  • Store the OAuth secret in Meteor.settings.public. The client sees public. Put secrets at the top level of settings.json.
  • Build a custom password hash. accounts-password uses bcrypt; trust it.
  • Bypass forbidClientAccountCreation with a method that takes a password. Same risk.
  • Run Accounts.config({ ... }) inside a deferred path. Call it at top level or in Meteor.startup.

See also

  • references/password-flows.md
  • references/oauth-services.md
  • references/eval-cases.md
  • For OAuth-secret encryption and CSP, see the meteor-security skill.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.