Framework-agnostic HTTP core for Siran Auth - types, handlers, and port interfaces
npm install @siran/auth-httpFramework-agnostic HTTP core for Siran Auth - types, handlers, and port interfaces.
This package provides the foundational HTTP layer for authentication without any framework dependencies. It contains:
- HTTP Abstractions: HttpRequest and HttpResponse interfaces for framework adapters to implement
- Request/Response DTOs: Type-safe request bodies and response structures
- Port Interfaces: Contracts for optional extensions (token providers, session stores)
- Core Handler Logic: Framework-agnostic AuthHandlers class
- Error Mapping: AuthErrorCode → HTTP status codes with user-friendly messages
- Request Parsing: Utilities to convert request bodies to AuthMethod types
``
@siran/auth-http (this package)
├── Core handlers and types
├── Port interfaces (contracts)
└── Error mapping utilities
@siran/auth-http-express (separate package)
├── Express middleware/routes
└── Maps Express Request/Response to HttpRequest/HttpResponse
@siran/auth-http-jwt (separate package)
├── Implements TokenProvider port
└── JWT token generation and verification
@siran/auth-http-sessions (separate package)
├── Implements SessionStore port
├── Redis session store
└── In-memory session store (development)
`
`bash`
npm install @siran/auth-http @siran/auth-core
`typescript
import { AuthHandlers } from '@siran/auth-http';
import { createAuthEngine } from '@siran/auth-core';
const authEngine = createAuthEngine({
// Configuration...
});
const handlers = new AuthHandlers({ authEngine });
`
`typescript
import { AuthHandlers } from '@siran/auth-http';
import { JwtTokenProvider } from '@siran/auth-http-jwt';
const tokenProvider = new JwtTokenProvider({
secret: process.env.JWT_SECRET,
});
const handlers = new AuthHandlers({
authEngine,
tokenProvider,
sessionExpiresIn: 3600, // 1 hour
});
`
`typescript
import { AuthHandlers } from '@siran/auth-http';
import { RedisSessionStore } from '@siran/auth-http-sessions';
const sessionStore = new RedisSessionStore({
redis: redisClient,
});
const handlers = new AuthHandlers({
authEngine,
sessionStore,
sessionExpiresIn: 86400, // 24 hours
});
`
`typescript
import express from 'express';
import { AuthHandlers } from '@siran/auth-http';
const app = express();
app.use(express.json());
const handlers = new AuthHandlers({ authEngine });
app.post('/api/auth/login', async (req, res) => {
const result = await handlers.login(req.body);
res.status(result.status).json(result.body);
});
app.post('/api/auth/register', async (req, res) => {
const result = await handlers.register(req.body);
res.status(result.status).json(result.body);
});
app.post('/api/auth/logout', async (req, res) => {
const result = await handlers.logout(req.body.sessionId);
res.status(result.status).json(result.body);
});
`
Main handler class that wraps AuthEngine and provides HTTP-compatible methods.
`typescript
class AuthHandlers {
constructor(options: HandlerOptions);
login(body: LoginRequestBody): Promise
register(body: RegisterRequestBody): Promise
logout(sessionId?: string, userId?: string): Promise
}
`
`typescript
type LoginRequestBody =
| { type: 'password'; identifier: string; password: string }
| { type: 'otp'; identifier: string; code: string }
| { type: 'magic_link'; token: string }
| { type: 'oauth'; provider: 'google' | 'github' | 'apple' | 'facebook'; code: string };
type RegisterRequestBody = LoginRequestBody;
`
`typescript
interface AuthSuccessResponse {
ok: true;
user: UserAccount;
token?: string;
session?: {
id: string;
expiresAt: string;
};
}
interface AuthErrorResponse {
ok: false;
error: AuthErrorCode;
message: string;
violatedPolicies?: string[];
}
type AuthResponse = AuthSuccessResponse | AuthErrorResponse;
`
All AuthErrorCode values are mapped to appropriate HTTP status codes:
| Error Code | HTTP Status | Reason |
|---|---|---|
| INVALID_CREDENTIALS | 401 | Invalid username/password |ACCOUNT_NOT_VERIFIED
| | 403 | Email not verified |ACCOUNT_DISABLED
| | 403 | Account disabled |ACCOUNT_LOCKED
| | 403 | Account locked due to failed attempts |INVALID_METHOD_PAYLOAD
| | 400 | Invalid request format |MISSING_PASSWORD
| | 400 | Missing required field |WEAK_PASSWORD
| | 422 | Password doesn't meet requirements |ACCOUNT_ALREADY_EXISTS
| | 409 | Account already exists |RATE_LIMIT_EXCEEDED
| | 429 | Too many attempts |INTERNAL_ERROR
| | 500 | Server error |
#### TokenProvider
Implement this to provide custom token generation:
`typescript`
interface TokenProvider {
generate(user: UserAccount, expiresIn?: number): Promise
verify(token: string): Promise
decode(token: string): TokenPayload | null;
}
#### SessionStore
Implement this to provide custom session storage:
`typescript`
interface SessionStore {
create(userId: string, expiresIn: number): Promise
get(sessionId: string): Promise
delete(sessionId: string): Promise
deleteByUser(userId: string): Promise
refresh(sessionId: string, expiresIn: number): Promise
}
`typescript
import { parseLoginRequest, parseRegisterRequest } from '@siran/auth-http';
const authMethod = parseLoginRequest(req.body);
// Throws ParseError if invalid
`
`typescript
import { mapAuthErrorToHttpStatus, getErrorMessage } from '@siran/auth-http';
const status = mapAuthErrorToHttpStatus('INVALID_CREDENTIALS'); // 401
const message = getErrorMessage('INVALID_CREDENTIALS');
// "Invalid credentials. Please check your identifier and password."
`
Run tests:
`bash`
nx test @siran/auth-http
Run tests with coverage:
`bash`
nx test @siran/auth-http --coverage
The package includes comprehensive tests for:
- Error mapping (all error codes to HTTP status codes)
- Request parsing (all auth method types)
- Handler logic (login, register, logout with various scenarios)
- Token generation and session creation
- Error handling and edge cases
To create an adapter for a new framework:
1. Import AuthHandlers and types from @siran/auth-httpHttpRequest
2. Implement and HttpResponse interfaces
3. Create middleware/routes that call the handler methods
4. Map framework-specific request/response to the HTTP abstractions
Example structure:
`typescript
// adapters/express.ts
import type { Request, Response } from 'express';
import type { HttpRequest, HttpResponse } from '@siran/auth-http';
function mapExpressRequest(req: Request): HttpRequest {
return {
body: req.body,
headers: req.headers as Record
method: req.method,
url: req.url,
};
}
function mapExpressResponse(res: Response): HttpResponse {
return {
status: (code: number) => {
res.status(code);
return mapExpressResponse(res);
},
json: (data: unknown) => {
res.json(data);
},
setHeader: (name: string, value: string) => {
res.setHeader(name, value);
return mapExpressResponse(res);
},
};
}
`
- @siran/auth-core: Core authentication engine and types
Build the package:
`bash`
nx build @siran/auth-http
Type checking:
`bash``
nx typecheck @siran/auth-http
Part of the Siran Auth project.