@glitch_protocol/auth-core
v0.2.0
Published
Core types, interfaces, and constants for glitch_protocol
Downloads
642
Maintainers
Readme
@glitch_protocol/auth-core
Core interfaces, types, and constants for the glitch_protocol SDK. Provides the pluggable adapter interfaces (DatabaseAdapter, CacheAdapter, SocketBroadcaster) that all other packages depend on.
Install
npm install @glitch_protocol/auth-coreInterfaces
DatabaseAdapter
Abstracts all database operations. Implement this to use your own database.
export interface DatabaseAdapter {
// User operations
getUser(userId: string): Promise<AuthUser | null>;
// Session CRUD
createSession(
userId: string,
deviceName: string | null,
ipAddress: string,
userAgent: string | undefined,
browser: string | null,
os: string | null,
expiresAt: Date,
): Promise<Session>;
getActiveSessions(userId: string): Promise<Session[]>;
getSessionById(sessionId: string): Promise<Session | null>;
isSessionActive(sessionId: string): Promise<boolean>;
deactivateSession(sessionId: string): Promise<void>;
updateSessionActivity(sessionId: string): Promise<void>;
updateSessionSocketId(sessionId: string, socketId: string | null): Promise<void>;
// Multi-device ops
getOtherSessions(userId: string, excludeSessionId: string): Promise<Session[]>;
deactivateOtherSessions(userId: string, excludeSessionId: string): Promise<void>;
// Cleanup
getExpiredSessions(now: Date): Promise<Session[]>;
// Refresh token CRUD
createRefreshToken(
userId: string,
sessionId: string,
hashedToken: string,
expiresAt: Date,
rotatedFrom?: string,
): Promise<RefreshToken>;
getRefreshToken(hashedToken: string): Promise<RefreshToken | null>;
revokeRefreshToken(tokenId: string): Promise<void>;
revokeSessionTokens(sessionId: string): Promise<void>;
revokeAllUserTokens(userId: string): Promise<void>;
deactivateAllUserSessions(userId: string): Promise<void>;
}Existing implementation: @glitch_protocol/auth-adapter-drizzle
To implement your own: extend this interface and pass it to glitch_protocolEngine.
CacheAdapter
Optional caching and distributed locking. System works without it but degrades to DB-only.
export interface CacheAdapter {
// Cache ops
get(key: string): Promise<string | null>;
set(key: string, value: string, ttlSeconds: number): Promise<void>;
delete(key: string): Promise<void>;
deleteMany(keys: string[]): Promise<void>;
// Distributed locking (for cleanup job)
acquireLock(key: string, ttlSeconds: number): Promise<boolean>;
releaseLock(key: string): Promise<void>;
// Availability check
isAvailable(): Promise<boolean>;
}Existing implementation: @glitch_protocol/auth-adapter-redis
To implement your own: extend this interface. All methods should swallow errors gracefully so the system falls back to DB-only operation.
SocketBroadcaster
Real-time event broadcasting. Emit to rooms, disconnect sockets.
export interface SocketBroadcaster {
// Emit event to everyone in a room
emitToRoom(room: string, event: string, data: unknown): Promise<void>;
// Disconnect a single socket
disconnectSocket(socketId: string, close?: boolean): Promise<void>;
// Disconnect all sockets in a room (cross-instance safe via Redis adapter)
disconnectRoom(room: string, close?: boolean): Promise<void>;
}To implement: Use with Socket.IO Redis adapter for multi-instance support.
Example (Socket.IO + Redis):
import { createAdapter } from "@socket.io/redis-adapter";
const io = new Server(server);
io.adapter(createAdapter(pubClient, subClient)); // Redis adapter enables cross-instance broadcasting
const socketBroadcaster: SocketBroadcaster = {
emitToRoom: async (room, event, data) => io.to(room).emit(event, data),
disconnectSocket: async (socketId) => io.sockets.sockets.get(socketId)?.disconnect(),
disconnectRoom: async (room) => io.in(room).disconnectSockets(),
};Types
AuthUser
interface AuthUser {
id: string;
email: string;
name: string;
}Session
interface Session {
id: string;
userId: string;
deviceName: string | null;
ipAddress: string;
userAgent: string | undefined;
browser: string | null;
os: string | null;
isActive: boolean;
socketId: string | null;
lastActivity: Date;
expiresAt: Date;
createdAt: Date;
updatedAt: Date;
}SessionInfo
Client-safe session metadata (excludes userAgent, userId, updatedAt).
interface SessionInfo {
id: string;
deviceName: string | null;
browser: string | null;
os: string | null;
ipAddress: string;
isActive: boolean;
isCurrent: boolean;
lastActivity: string; // ISO string
createdAt: string; // ISO string
}RefreshToken
interface RefreshToken {
id: string;
userId: string;
sessionId: string;
token: string; // SHA-256 hash
rotatedFrom: string | null;
isRevoked: boolean;
revokedAt: Date | null;
expiresAt: Date;
createdAt: Date;
updatedAt: Date;
}TokenPair
interface TokenPair {
accessToken: string; // JWT
refreshToken: string; // Raw 64-byte hex token
}ApiResponse
Standard API response shape.
type ApiResponse<T> =
| { success: true; data: T }
| { success: false; error: string; code?: string };Constants
SOCKET_EVENTS
{
SESSION_NEW: "session:new",
SESSION_TERMINATED: "session:terminated",
SESSION_LIST_UPDATED: "session:list-updated",
SESSION_HEARTBEAT: "session:heartbeat",
CONNECTION_ERROR: "connection:error",
}REDIS_KEYS
Functions that generate Redis key names.
{
USER: (userId: string) => `user:${userId}`,
SESSION_ACTIVE: (sessionId: string) => `session:${sessionId}:active`,
ACTIVE_SESSIONS: (userId: string) => `sessions:active:${userId}`,
REFRESH_TOKEN: (hashedToken: string) => `rt:hash:${hashedToken}`,
HEARTBEAT: (sessionId: string) => `heartbeat:${sessionId}`,
SESSION_ROOM: (sessionId: string) => `session:${sessionId}`,
CLEANUP_LOCK: () => "cleanup:sessions:lock",
}REDIS_TTL
Cache expiry times in seconds.
{
USER_PROFILE: 3600, // 1 hour
SESSION_ACTIVE_CHECK: 300, // 5 minutes
ACTIVE_SESSIONS: 300, // 5 minutes
REFRESH_TOKEN: 2592000, // 30 days
HEARTBEAT_DEBOUNCE: 25, // 25 seconds
CLEANUP_LOCK: 840, // 14 minutes
}ERROR_CODES
{
TOKEN_INVALID: "TOKEN_INVALID",
TOKEN_EXPIRED: "TOKEN_EXPIRED",
TOKEN_REUSE_DETECTED: "TOKEN_REUSE_DETECTED",
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
USER_NOT_FOUND: "USER_NOT_FOUND",
SESSION_EXPIRED: "SESSION_EXPIRED",
UNAUTHORIZED: "UNAUTHORIZED",
FORBIDDEN: "FORBIDDEN",
}Implementing a Custom DatabaseAdapter
import type { DatabaseAdapter } from "@glitch_protocol/auth-core";
export class CustomDatabaseAdapter implements DatabaseAdapter {
async getUser(userId: string) {
// Fetch from your database
return { id: userId, email: "...", name: "..." };
}
async createSession(userId, deviceName, ipAddress, userAgent, browser, os, expiresAt) {
// Insert into your database
return { id: "...", userId, ... };
}
// ... implement all 17 methods
}
// Use it:
const adapter = new CustomDatabaseAdapter();
const engine = new glitch_protocolEngine(adapter, cache, socket, config);Implementing a Custom CacheAdapter
import type { CacheAdapter } from "@glitch_protocol/auth-core";
export class InMemoryCacheAdapter implements CacheAdapter {
private cache = new Map<string, string>();
private ttls = new Map<string, number>();
async get(key: string) {
const expiry = this.ttls.get(key);
if (expiry && expiry < Date.now()) {
this.cache.delete(key);
return null;
}
return this.cache.get(key) ?? null;
}
async set(key: string, value: string, ttlSeconds: number) {
this.cache.set(key, value);
this.ttls.set(key, Date.now() + ttlSeconds * 1000);
}
async delete(key: string) {
this.cache.delete(key);
this.ttls.delete(key);
}
// ... implement remaining methods
}
// Use it:
const cache = new InMemoryCacheAdapter();
const engine = new glitch_protocolEngine(db, cache, socket, config);Exports
All types and interfaces are re-exported from the main entry point:
import {
DatabaseAdapter,
CacheAdapter,
SocketBroadcaster,
AuthUser,
Session,
SessionInfo,
RefreshToken,
TokenPair,
ApiResponse,
SOCKET_EVENTS,
REDIS_KEYS,
REDIS_TTL,
ERROR_CODES,
} from "@glitch_protocol/auth-core";Node Version
ESM-only. Requires Node.js >=18.
