X Enterprises
fastify-xauth-local

local.login

Built-in POST route that authenticates a user with email and password and returns a signed JWT.

local.login

Built-in POST {loginPath} route registered when local.enabled is true. Accepts an email and password, verifies against the userLookup function and bcrypt hash, and returns a signed JWT plus a safe user object.

Default path: {prefix}/local (e.g. /api/local)

Signature

POST {loginPath}
Content-Type: application/json

{
  "email": string,    // required, valid email format
  "password": string  // required, non-empty
}

Params

NameTypeRequiredDescription
emailstringYesUser's email address
passwordstringYesUser's plaintext password

Returns

200 OK:

{
  "token": "eyJ...",
  "user": {
    "id": 1,
    "email": "user@example.com",
    "first_name": "Jane",
    "last_name": "Doe",
    "admin": false,
    "color": "#abc",
    "scope": ["user"]
  }
}

Throws

StatusMessageReason
401Invalid email or passwordUser not found or password mismatch
500An error occurred during loginUnexpected server error

This route is automatically excluded from JWT verification so it can be accessed without a token.

Examples

Basic: login request

curl -X POST http://localhost:3000/api/local \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"secret123"}'
// Use the returned token in subsequent requests
const { token } = await fetch("/api/local", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email, password }),
}).then((r) => r.json());

localStorage.setItem("token", token);

Advanced: plugin registration with userLookup

await fastify.register(xAuthLocal, {
  configs: [
    {
      name: "api",
      prefix: "/api",
      secret: process.env.JWT_SECRET,
      local: {
        enabled: true,
        // userLookup must return an object with a 'password' field (bcrypt hash)
        userLookup: async (email) => {
          const user = await db.query(
            "SELECT * FROM users WHERE email = $1",
            [email]
          );
          return user.rows[0] ?? null;
        },
      },
    },
  ],
});
// Login route is now available at POST /api/local

See Also

AI Context

package: "@xenterprises/fastify-xauth-local"
route: POST {loginPath} (default /api/local)
use-when: Built-in login route — authenticates with email + password and returns a signed JWT
requires: local.enabled: true, local.userLookup function
returns: { token: string }
Copyright © 2026