@simpleauthjs/core
v0.0.1
Published
JavaScript SDK for SimpleAuth
Readme
@simpleauthjs/core
A tiny JavaScript SDK for the SimpleAuth auth API (/api/external/auth/*).
Installation
npm install @simpleauthjs/core60-second quickstart
import { createSimpleAuthClient } from "@simpleauthjs/core"
const auth = createSimpleAuthClient({
apiKey: "sa_live_your_key",
})
await auth.register({
email: "[email protected]",
password: "password123",
name: "Demo User",
})
await auth.login({ email: "[email protected]", password: "password123" })
console.log(await auth.me())
await auth.logout()Client options
createSimpleAuthClient({
apiKey: string, // required — sent in headers only (never in JSON body)
baseUrl?: string, // default: "https://simpleauth.net" (falls back to SIMPLEAUTH_BASE_URL env)
apiKeyTransport?: "authorization" | "x-api-key", // default: "authorization"
fetch?: typeof fetch, // custom fetch implementation
credentials?: "include" | ... , // default: "include"
headers?: Record<string, string>,
timeoutMs?: number,
onError?: (error: SimpleAuthError) => void,
onRequest?: (event) => void, // request tracing hook
onResponse?: (event) => void, // response tracing hook
debug?: boolean, // debug logging in console
})Base URL resolution
The baseUrl is resolved in this order:
- Explicit
baseUrloption passed to the constructor. process.env.SIMPLEAUTH_BASE_URLenvironment variable (optional; use when pointing at a local SimpleAuth server, for examplehttp://localhost:3000)."https://simpleauth.net"(default for the hosted API).
API key in requests
The app API key is sent using:
apiKeyTransport: "authorization"(default):Authorization: Bearer <apiKey>apiKeyTransport: "x-api-key":X-API-Key: <apiKey>
The server accepts either header. Credentials and user payloads (email, password, etc.) stay in the JSON body; the key is never duplicated there.
Apps have two key types in the dashboard: a public key (sa_live_…) for clients and this package's default client, and a secret key (sa_secret_…) for trusted backends. User provisioning (createUser) lives under @simpleauthjs/core/server and uses the secret key (see below).
Example with options
const auth = createSimpleAuthClient({
apiKey: "sa_live_your_key",
timeoutMs: 10_000,
onError: (error) => {
console.error("SDK error:", error.status, error.code, error.message)
},
onRequest: (event) => {
console.log("request", event)
},
onResponse: (event) => {
console.log("response", event.status, `${event.durationMs}ms`)
},
})DX notes
- SDK validates required input fields and throws early (
email,password,token, etc.) for faster feedback. - Sensitive values are redacted from debug/request traces (
apiKey,password,token). - If your frontend runs on
https://andbaseUrluseshttp://, SDK prints a warning.
Exposed methods
Core auth
register({ email, password, name? })login({ email, password })me()logout()deleteAccount({ password })— requires session cookies; confirms password then deletes the user (see below).isAuthenticated()
Recovery and verification
forgotPassword({ email })resetPassword({ token, password })— sends the app API key; the reset token must belong to that app.sendVerificationEmail({ email })— also available asresendVerification(alias)verifyEmail({ token })— sends the app API key; the verification token must belong to that app.
All methods return promises.
Method examples
Register
const result = await auth.register({
email: "[email protected]",
password: "password123",
name: "Demo",
})
console.log(result.user)Create user (server-only, secret key)
Import from @simpleauthjs/core/server (not @simpleauthjs/core). This entry is for Node, Edge, and other server runtimes only; it throws if loaded in a browser bundle.
It uses the same base URL resolution as the client (baseUrl → SIMPLEAUTH_BASE_URL → https://simpleauth.net). The secret API key (sa_secret_…) comes from SIMPLEAUTH_SECRET_KEY / secretKey and is required for admin methods below. You can omit the secret key if you only use currentUser() (session cookies).
Same payload and { user } response as register; createUser calls POST /api/external/auth/admin/users and emits the same user.registered webhook server-side.
import { createServerSimpleAuthClient } from "@simpleauthjs/core/server"
const adminAuth = createServerSimpleAuthClient()
// or: createServerSimpleAuthClient({ secretKey }) to override env
await adminAuth.createUser({
email: "[email protected]",
password: "temporarySecret123",
name: "New User",
})Other admin HTTP routes (secret key only):
| Method | Path | SDK |
|--------|------|-----|
| POST | /api/external/auth/admin/users/:id/ban body { durationMinutes } | banUser(externalUserId, { durationMinutes }) |
| POST | /api/external/auth/admin/users/:id/unban | unbanUser(externalUserId) |
| GET | /api/external/auth/admin/users/:id | getUserById(externalUserId) — returns { user } with bannedUntil |
| DELETE | /api/external/auth/admin/users/:id | deleteUserById(externalUserId) — permanent delete; user.deleted webhook |
| GET | /api/external/auth/admin/users/:id/sessions | listUserSessions(externalUserId) — metadata only (no session tokens) |
| DELETE | /api/external/auth/admin/users/:id/sessions/:sessionId | deleteUserSession(externalUserId, sessionId) — revoke that login |
| POST | /api/external/auth/admin/users/:id/revoke-all-sessions | revokeAllUserSessions(externalUserId) — { success, revokedCount } |
await adminAuth.banUser("ext_user_id", { durationMinutes: 60 })
await adminAuth.unbanUser("ext_user_id")
const { user } = await adminAuth.getUserById("ext_user_id")
const { sessions } = await adminAuth.listUserSessions("ext_user_id")
await adminAuth.deleteUserSession("ext_user_id", sessions[0].id)
await adminAuth.revokeAllUserSessions("ext_user_id")
await adminAuth.deleteUserById("ext_user_id")Current signed-in user (currentUser)
For server-rendered pages and API routes, call currentUser(requestLike) to load the same user as the browser me endpoint. It performs GET /api/external/auth/me and forwards session cookies from the incoming request (it does not send the secret API key). Pass the Web Request from your handler, or { cookie: "..." }, or { headers } (e.g. Next.js headers()).
import { createServerSimpleAuthClient, SimpleAuthError } from "@simpleauthjs/core/server"
const auth = createServerSimpleAuthClient()
export async function GET(request) {
try {
const { user } = await auth.currentUser(request)
return Response.json(user)
} catch (e) {
if (e instanceof SimpleAuthError && e.status === 401) {
return new Response("Unauthorized", { status: 401 })
}
throw e
}
}Cookies sa_ext_session and sa_ext_app must be present on the request (set after login when the client talks to your app with credentials).
Login
await auth.login({
email: "[email protected]",
password: "password123",
})If the account is suspended (banned), the server responds with 403 and an error message such as "Account is suspended". Your UI should treat this like a failed login.
Current user
const me = await auth.me()
console.log(me.user)Logout
await auth.logout()Delete account
Requires a logged-in session (login first) and the user's current password. The app API key is not sent on this request (only session cookies). On success the server clears auth cookies and removes the user; your app should stop using this client for that user.
await auth.deleteAccount({ password: "currentPassword123" })Forgot/reset password
forgotPassword and resetPassword both send the app API key in headers. The server resolves the app from the key; the reset link token must have been issued for that same app, and the user must still exist.
await auth.forgotPassword({ email: "[email protected]" })
await auth.resetPassword({
token: "password-reset-token",
password: "newPassword123",
})Email verification
sendVerificationEmail sends the app API key. verifyEmail also sends the app API key; the token in the link must be for that same app.
await auth.sendVerificationEmail({ email: "[email protected]" })
await auth.verifyEmail({ token: "email-verification-token" })Error handling
SDK methods throw SimpleAuthError.
import { createSimpleAuthClient, SimpleAuthError } from "@simpleauthjs/core"
try {
await auth.login({ email: "[email protected]", password: "wrong" })
} catch (error) {
if (error instanceof SimpleAuthError) {
console.error(error.status, error.code, error.message)
console.error(error.details)
}
}Browser cookie requirements
SimpleAuth external sessions use HttpOnly cookies. In browser apps:
- Keep
credentials: "include"(SDK default). - Ensure backend CORS allows credentials for your frontend origin.
- Ensure same-site/domain settings match your deployment topology.
Migration note
If you were using legacy SDK routes under /api/saas/auth/*, migrate to this SDK version which targets /api/external/auth/*.
If you previously sent the app API key in the JSON body yourself (without this SDK), switch to Authorization: Bearer or X-API-Key on the server; this SDK already uses headers only.
Troubleshooting
401 Unauthorized on me()
- Check that
login()succeeded. - Confirm browser is sending cookies (
credentials: include). - Confirm server CORS and cookie config.
Invalid API key
- Ensure you are using the current active app key (rotating a key revokes prior key).
Request timeout
- Increase
timeoutMswhen creating the client.
Integration fixture (Node script)
A runnable fixture is available at:
packages/simpleauth-js/examples/node-script/demo.mjs
Run it with:
SIMPLEAUTH_API_KEY=sa_live_your_key \
node packages/simpleauth-js/examples/node-script/demo.mjsFor createServerSimpleAuthClient, set SIMPLEAUTH_SECRET_KEY in the environment instead of SIMPLEAUTH_API_KEY.
Local SDK tests
cd packages/simpleauth-js
npm test