Skip to content

Authentication ​

Before using emusks, you must authenticate your client. emusks supports two authentication methods: auth token login and username/password login.

Auth token login ​

The simplest way to authenticate is with a Twitter/X auth token. This is a cookie value (auth_token) from an active browser session.

js
import Emusks from "emusks";

const client = new Emusks();
await client.login("your_auth_token_here");

You may also provide an object if you'd like to configure proxies or a custom client:

js
await client.login({
  auth_token: "your_auth_token_here",
  // e.g. proxy: "protocol://user:pass@host:port",
});

Finding your auth token ​

  1. Open x.com in your browser and log in
  2. Open Developer Tools (F12 or Cmd+Shift+I)
  3. Go to Application → Cookies → https://x.com
  4. Find the cookie named auth_token
  5. Copy its value

Username & password login ​

You can also log in with your account credentials. the default password flow uses Jetfuel and supports two-factor authentication, email verification, and alternate-identifier challenges.

js
const client = new Emusks();
await client.login({
  type: "password",
  username: "your_username",
  password: "your_password",
});

browser-free login with a local profile ​

as of 2.3.8, emusks can generate fresh Castle tokens locally from a saved signal profile. the verified browser-free path requires a compatible private profile. the profile is not included in npm, and the unprofiled DOM sandbox is not a verified replacement.

place your profile at .emusks/castle-profile.json in the working directory, set EMUSKS_CASTLE_PROFILE, or pass its path as castleProfile:

js
await client.login({
  type: "password",
  username: process.env.X_USERNAME,
  password: process.env.X_PASSWORD,
  castleProfile: "/private/path/castle-profile.json",
  onRequest: async (type) => prompt(type),
});

the profile holds previously collected browser measurements. request-specific fields and tokens are generated locally; login does not launch or contact a browser. profile-backed headers are retained for authenticated API calls. an SDK hash mismatch stops login before credential submission, so an SDK update requires a matching profile. keep this file private and ignore .emusks/ in version control. castleProfile: false disables profile loading.

2FA & email verification ​

If your account has two-factor authentication enabled or Twitter requests an email/phone verification, use the onRequest callback to provide the required codes:

js
const client = new Emusks();
await client.login({
  type: "password",
  username: "your_username",
  password: "your_password",
  onRequest: async (type) => {
    if (type === "two_factor_code") {
      // Return your 2FA code (e.g. from an authenticator app)
      return "123456";
    }
    if (type === "email_code") {
      // Return the code sent to your email
      return "654321";
    }
  },
});

onRequest is blocking. If Twitter asks for a code, your callback must return it before the login can continue.

handle email_code, two_factor_code, and alternate_identifier in onRequest when those values are available. the flow stops on server errors instead of resubmitting rejected credentials.

If you do rely on this, I also recommend setting all data so it can handle as much of the login flow as possible without needing to prompt you:

js
const client = new Emusks();
await client.login({
  type: "password",
  username: "your_username",
  password: "your_password",
  email: "your_email@example.com",
  phone: "+1234567890",
  onRequest: async (type) => {
    if (type === "two_factor_code") return "123456";
  },
});

knowledge check (identity challenge) ​

before showing the password step, X sometimes inserts a finish_knowledge_check: it finds your account from the username or email you passed, then asks you to confirm one specific identifier tied to it. which one it wants varies by account: Username, Email, or Phone.

onRequest receives that detail as an optional second argument, so you can answer the right one instead of guessing:

js
await client.login({
  type: "password",
  username: "your_username",
  password: "your_password",
  onRequest: async (type, context) => {
    if (type === "alternate_identifier") {
      if (context?.challengeType === "Username") return "your_handle";
      if (context?.challengeType === "Phone") return "+1234567890";
      if (context?.challengeType === "Email") return "your_email@example.com";
    }
    if (type === "email_code") return "654321";
  },
});

context is { challengeType, accountFound, accountFoundType }, where accountFound is the value X matched and accountFoundType is "email" or "username". the second argument is optional: existing (type) => ... callbacks keep working unchanged.

if you return nothing for alternate_identifier, emusks takes X's "use password" bypass and continues to the password step, skipping the check. that works for low-risk accounts; a flagged account may re-issue the check instead.

Reference ​

OptionTypeDescription
auth_tokenstringYour Twitter/X auth token (use directly as the argument to login())
typestringSet to "password" for username/password login
usernamestringYour Twitter/X username
passwordstringYour account password
emailstringEmail for alternate identifier challenges
phonestringPhone number for alternate identifier challenges
onRequestfunctionAsync callback (type, context?) for interactive login challenges. type is two_factor_code, email_code, or alternate_identifier; context (on alternate_identifier) is { challengeType, accountFound, accountFoundType }.

Elevated Access ​

Some sensitive actions, like reading settings, require elevated access. After logging in, call elevate() with your password:

js
await client.login("your_auth_token");

// Elevate your session for sensitive operations
await client.elevate("your_password");

// Now you can perform privileged actions

TIP

You only need to elevate once per session. The elevated state persists until the session ends.

Checking your session ​

After logging in, you can verify your session by fetching your own profile:

js
const me = await client.account.viewer();
console.log(`Logged in as @${me.username}`);
console.log(`Followers: ${me.stats.followers.count}`);

or by checking the output of the client.login() method, which returns your user object on successful authentication.

Next steps ​

Head over to Configuration to learn how to choose which client to emulate, set up proxies, and customize your setup.

not affiliated with X Corp.