@nyx-auth/core
v0.2.0
Published
The core library for nyx-auth. Provides the essential session management functionality
Maintainers
Readme
@nyx-auth/core
[!IMPORTANT]
Nyx currently is just a reimplementation of lucia with a few personal modifications. The API is not stable and may change in the future. Use at your own risk.
The core library for nyx-auth. Provides session management and user management via a namespaced API.
Installation
bun add @nyx-auth/coreUsage
Create instance & Infer types
import { Nyx, UnexpectedError } from "@nyx-auth/core";
import { DrizzleAdapter } from "@nyx-auth/drizzle-adapter"; // or any other adapter
const nyx = new Nyx({
adapter: DrizzleAdapter.sqlite({ db, tables: { sessions, users } }),
session: {
mapSessionAttributes: (attributes) => ({ ipAddress: attributes.ipAddress }),
},
user: {
mapUserAttributes: (attributes) => ({
email: attributes.email,
}),
},
});
// Type inference
type Session = typeof nyx.session.$infer;
type User = typeof nyx.user.$infer;Session management
const sessionResult = await nyx.session.create(userId, { ipAddress: "..." });
if (sessionResult instanceof UnexpectedError) {
// handle error
}
const { token, value: session } = sessionResult;
const validated = await nyx.session.validateToken(token);
if (validated instanceof UnexpectedError) {
// handle error
}
if (validated) {
const { session, user } = validated;
}
const deleted = await nyx.session.invalidate(sessionId); // false if no session matched
if (deleted instanceof UnexpectedError) {
// handle error — the session may not have been revoked
}
const allDeleted = await nyx.session.invalidateAll(userId); // false if the user had no sessions
if (allDeleted instanceof UnexpectedError) {
// handle error — sessions may still be active
}
await nyx.session.updateAttributes(sessionId, { ipAddress: "..." });User management
const user = await nyx.user.create({ email, passwordHash });
if (user instanceof UnexpectedError) {
// handle error
}
const existingUser = await nyx.user.get(userId);
if (existingUser instanceof UnexpectedError) {
// handle error
}
await nyx.user.updateAttributes(userId, { email: "[email protected]" });
await nyx.user.delete(userId);[!IMPORTANT]
mapUserAttributesandmapSessionAttributesare required. They map the raw user/session table columns (except reserved ones) to what your app receives fromvalidateToken()anduser.get(). Without them, every column — includingpasswordHash, TOTP secrets, or OAuth provider tokens — would be returned to your application and could leak to any client you serialize the user object to. Only map the fields your app needs.
TimeSpan
A helper for specifying time durations in configuration options.
import { TimeSpan } from "@nyx-auth/core";
const span = new TimeSpan(30, "m"); // 30 minutes
const span = new TimeSpan(7, "d"); // 7 days| Unit | TimeSpanUnit |
| ---- | ------------ |
| ms | milliseconds |
| s | seconds |
| m | minutes |
| h | hours |
| d | days |
| w | weeks |
Methods:
| Method | Returns |
| ------------------------------------------- | --------- |
| milliseconds() | number |
| seconds() | number |
| toDate() | Date |
| elapsedSince(from: Date, now: Date) | boolean |
Error handling
Every method returns errors as return values (not thrown exceptions). Check the result type to handle failures.
import { UnexpectedError } from "@nyx-auth/core";
const result = await nyx.session.create(userId, { ipAddress });
if (result instanceof UnexpectedError) {
// The underlying cause is available via result.cause
}Custom adapters
Implement the Adapter interface to support any database or storage backend.
import {
type Adapter,
type Attributes,
type DatabaseSession,
type DatabaseUser,
AdapterError,
} from "@nyx-auth/core";Adapter
| Method | Returns |
| -------------------------------------------------------- | ----------------------------------------------------- |
| insertSession(session) | DatabaseSession or AdapterError |
| updateSessionbyId(sessionId, session) | undefined or AdapterError |
| deleteSessionById(sessionId) | boolean (deleted?) or AdapterError |
| deleteSessionsByUserId(userId) | boolean (deleted?) or AdapterError |
| insertUser(user) | DatabaseUser or AdapterError |
| findUserById(userId) | DatabaseUser or null or AdapterError |
| updateUserbyId(userId, user) | undefined or AdapterError |
| deleteUserById(userId) | undefined or AdapterError |
| findSessionWithUserById(sessionId) | { session, user } or null or AdapterError |
Type reference
// Shape of a raw session row from the database
interface DatabaseSession<A = object> {
id: string;
userId: string;
secretHash: Uint8Array;
createdAt: Date;
lastVerifiedAt: Date;
attributes: A;
}
// Shape of a raw user row from the database
interface DatabaseUser<A = object> {
id: string;
attributes: A;
}
// Defines the select and insert attribute shapes separately
type Attributes<Select, Insert> = {
select: Select;
insert: Insert;
};AdapterError wraps failures with an operation name and the underlying cause.
API
new Nyx(options)
| Option | Type | Default |
| ------------------------------- | ---------------------- | --------------------- |
| adapter | Adapter | (required) |
| session.inactivityTimeout | TimeSpan | 10 days |
| session.activityCheckInterval | TimeSpan | 1 hour |
| session.now | () => Date | () => new Date() |
| session.mapSessionAttributes | (db) => SessionAttrs | (required) |
| user.createId | () => string | crypto.randomUUID() |
| user.mapUserAttributes | (db) => UserAttrs | (required) |
session.now returns the current time used for expiry checks, creation
timestamps and lastVerifiedAt refresh. Override it in tests to control time
deterministically instead of sleeping against the real clock.
nyx.session
| Method | Returns |
| ----------------------------------------- | -------------------------------------------------- |
| create(userId, attributes) | { token, value } or UnexpectedError |
| validateToken(token) | { session, user } or null or UnexpectedError |
| invalidate(id) | boolean (deleted?) or UnexpectedError |
| invalidateAll(userId) | boolean (deleted?) or UnexpectedError |
| updateAttributes(sessionId, attributes) | undefined or UnexpectedError |
| $infer | Session<SessionAttrs> |
nyx.user
| Method | Returns |
| --------------------------------------------- | ------------------------------------------ |
| create(attributes) | User or UnexpectedError |
| get(id) | User or null or UnexpectedError |
| updateAttributes(id, attributes) | undefined or UnexpectedError |
| delete(id) | undefined or UnexpectedError |
| $infer | User<UserAttrs> |
Security guidance
- Only
validateTokenauthorizes. Session IDs are not secrets — never gate access with anything else. - Store the token in an
HttpOnly,Secure,SameSite=Lax(orStrict) cookie and never inlocalStorage, URLs, or logs. Read the token from the cookie on each request, validate it, and set a new cookie when the session is refreshed. - Invalidate sessions on credential changes. Call
invalidateAll(userId)when a user changes their password, email, or when you suspect compromise. Also call it on privilege escalation if you want to force re-authentication. - Mappers are required. The attribute mappers define exactly what your
app sees — only map the fields your app needs, and keep
passwordHash, TOTP secrets, provider tokens, etc. out of the mapped shape. - Delete the user's sessions when deleting a user.
validateTokenfails closed for orphaned sessions (the join finds no user), but addON DELETE CASCADEto the sessions table so rows don't accumulate. - Don't serialize
UnexpectedError.causeto clients. It can contain database internals (driver errors, query details). - Validate session tokens server-side on every request — the token is the only credential; there is no client-side state to trust.
session.inactivityTimeoutandsession.activityCheckIntervalmust both be greater than zero, and the check interval must be smaller than the timeout. Invalid configurations throw at construction time.
